diff options
author | 2019-01-30 16:17:50 +0000 | |
---|---|---|
committer | 2019-02-01 14:59:57 +0000 | |
commit | 2da3cbb4af20a64108e474c0bbbe0cc5d3af2aa2 (patch) | |
tree | 8cbdf50aab2183c701f1dc7c9ac17d1129fb5238 | |
parent | 0518af4e87d484b10e785aff9b030b688926cd7f (diff) |
hiddenapi: Fix class hierarchy traversal
`hiddenapi` builds and traverses the class hierarchy, visiting all
class members that methods/fields in stubs may resolve to.
The algorithm wouldn't work when:
* an interface declares a method which is in stubs, and
* a class implements the interface by inheriting the method from its
superclass; neither the class nor its superclass are in stubs.
The problem was that once a matching method was found, only subclasses
would be traversed. In this case, the method would be found in the
interface, the class which implements it would be traversed but its
superclass would not.
This patch simplifies the algorithm and partially reverts a performance
optimization which caused the problem. As a result, there is a build
time regression from 5s to 8s.
The patch also adds gtests which test this behaviour. There were no
tests until now because stubs are not present in master-art manifest.
Get around this issue by using the actual core JARs as stubs but test
the behaviour on other classes.
Bug: 122551864
Test: m test-art-host-gtest-hiddenapi_test
Change-Id: I63751c5ef517c8e9d3a157dfbec8de01bd99c2d4
-rw-r--r-- | build/Android.gtest.mk | 3 | ||||
-rw-r--r-- | test/HiddenApi/AbstractPackageClass.java | 19 | ||||
-rw-r--r-- | test/HiddenApi/PackageClass.java | 19 | ||||
-rw-r--r-- | test/HiddenApi/PublicInterface.java | 20 | ||||
-rw-r--r-- | test/HiddenApiStubs/HiddenApi | 19 | ||||
-rw-r--r-- | test/HiddenApiStubs/PublicInterface.java | 20 | ||||
-rw-r--r-- | tools/hiddenapi/hiddenapi.cc | 120 | ||||
-rw-r--r-- | tools/hiddenapi/hiddenapi_test.cc | 172 |
8 files changed, 274 insertions, 118 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 67d85c1a57..eac351110c 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -38,6 +38,7 @@ GTEST_DEX_DIRECTORIES := \ GetMethodSignature \ HiddenApi \ HiddenApiSignatures \ + HiddenApiStubs \ ImageLayoutA \ ImageLayoutB \ IMTA \ @@ -188,7 +189,7 @@ ART_GTEST_dexlayout_test_DEX_DEPS := ManyMethods ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps MainUncompressed EmptyUncompressed StringLiterals ART_GTEST_dex2oat_image_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle -ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi +ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi HiddenApiStubs ART_GTEST_hidden_api_test_DEX_DEPS := HiddenApiSignatures ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods VerifySoftFailDuringClinit ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB diff --git a/test/HiddenApi/AbstractPackageClass.java b/test/HiddenApi/AbstractPackageClass.java new file mode 100644 index 0000000000..8f955caa52 --- /dev/null +++ b/test/HiddenApi/AbstractPackageClass.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +abstract class AbstractPackageClass { + public void publicMethod2() {} +} diff --git a/test/HiddenApi/PackageClass.java b/test/HiddenApi/PackageClass.java new file mode 100644 index 0000000000..eece100b6e --- /dev/null +++ b/test/HiddenApi/PackageClass.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class PackageClass extends AbstractPackageClass implements PublicInterface { + public void publicMethod1() {} +} diff --git a/test/HiddenApi/PublicInterface.java b/test/HiddenApi/PublicInterface.java new file mode 100644 index 0000000000..77a37090ff --- /dev/null +++ b/test/HiddenApi/PublicInterface.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface PublicInterface { + void publicMethod1(); + void publicMethod2(); +} diff --git a/test/HiddenApiStubs/HiddenApi b/test/HiddenApiStubs/HiddenApi new file mode 100644 index 0000000000..6841ab5356 --- /dev/null +++ b/test/HiddenApiStubs/HiddenApi @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface PublicInterface { + void publicMethod(); +} diff --git a/test/HiddenApiStubs/PublicInterface.java b/test/HiddenApiStubs/PublicInterface.java new file mode 100644 index 0000000000..77a37090ff --- /dev/null +++ b/test/HiddenApiStubs/PublicInterface.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface PublicInterface { + void publicMethod1(); + void publicMethod2(); +} diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc index f426d02c5b..2692f6811a 100644 --- a/tools/hiddenapi/hiddenapi.cc +++ b/tools/hiddenapi/hiddenapi.cc @@ -124,6 +124,7 @@ class DexClass : public ClassAccessor { } inline bool IsPublic() const { return HasAccessFlags(kAccPublic); } + inline bool IsInterface() const { return HasAccessFlags(kAccInterface); } inline bool Equals(const DexClass& other) const { bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0; @@ -344,13 +345,13 @@ class HierarchyClass final { // See comment on Hierarchy::ForEachResolvableMember. template<typename Fn> bool ForEachResolvableMember(const DexMember& other, Fn fn) { - return ForEachResolvableMember_Impl(other, fn) != ResolutionResult::kNotFound; + std::vector<HierarchyClass*> visited; + return ForEachResolvableMember_Impl(other, fn, true, true, visited); } // Returns true if this class contains at least one member matching `other`. bool HasMatchingMember(const DexMember& other) { - return ForEachMatchingMember( - other, [](const DexMember&) { return true; }) != ResolutionResult::kNotFound; + return ForEachMatchingMember(other, [](const DexMember&) { return true; }); } // Recursively iterates over all subclasses of this class and invokes `fn` @@ -366,62 +367,60 @@ class HierarchyClass final { } private: - // Result of resolution which takes into account whether the member was found - // for the first time or not. This is just a performance optimization to prevent - // re-visiting previously visited members. - // Note that order matters. When accumulating results, we always pick the maximum. - enum class ResolutionResult { - kNotFound, - kFoundOld, - kFoundNew, - }; - - inline ResolutionResult Accumulate(ResolutionResult a, ResolutionResult b) { - return static_cast<ResolutionResult>( - std::max(static_cast<unsigned>(a), static_cast<unsigned>(b))); - } - template<typename Fn> - ResolutionResult ForEachResolvableMember_Impl(const DexMember& other, Fn fn) { + bool ForEachResolvableMember_Impl(const DexMember& other, + Fn fn, + bool allow_explore_up, + bool allow_explore_down, + std::vector<HierarchyClass*> visited) { + if (std::find(visited.begin(), visited.end(), this) == visited.end()) { + visited.push_back(this); + } else { + return false; + } + // First try to find a member matching `other` in this class. - ResolutionResult foundInClass = ForEachMatchingMember(other, fn); - - switch (foundInClass) { - case ResolutionResult::kFoundOld: - // A matching member was found and previously explored. All subclasses - // must have been explored too. - break; - - case ResolutionResult::kFoundNew: - // A matching member was found and this was the first time it was visited. - // If it is a virtual method, visit all methods overriding/implementing it too. - if (other.IsVirtualMethod()) { - for (HierarchyClass* subclass : extended_by_) { - subclass->ForEachOverridingMember(other, fn); - } - } - break; - - case ResolutionResult::kNotFound: - // A matching member was not found in this class. Explore the superclasses - // and implemented interfaces. - for (HierarchyClass* superclass : extends_) { - foundInClass = Accumulate( - foundInClass, superclass->ForEachResolvableMember_Impl(other, fn)); - } - break; + bool found = ForEachMatchingMember(other, fn); + + // If not found, see if it is inherited from parents. Note that this will not + // revisit parents already in `visited`. + if (!found && allow_explore_up) { + for (HierarchyClass* superclass : extends_) { + found |= superclass->ForEachResolvableMember_Impl( + other, + fn, + /* allow_explore_up */ true, + /* allow_explore_down */ false, + visited); + } } - return foundInClass; + // If this is a virtual method, continue exploring into subclasses so as to visit + // all overriding methods. Allow subclasses to explore their superclasses if this + // is an interface. This is needed to find implementations of this interface's + // methods inherited from superclasses (b/122551864). + if (allow_explore_down && other.IsVirtualMethod()) { + for (HierarchyClass* subclass : extended_by_) { + subclass->ForEachResolvableMember_Impl( + other, + fn, + /* allow_explore_up */ GetOneDexClass().IsInterface(), + /* allow_explore_down */ true, + visited); + } + } + + return found; } template<typename Fn> - ResolutionResult ForEachMatchingMember(const DexMember& other, Fn fn) { - ResolutionResult found = ResolutionResult::kNotFound; + bool ForEachMatchingMember(const DexMember& other, Fn fn) { + bool found = false; auto compare_member = [&](const DexMember& member) { + // TODO(dbrazdil): Check whether class of `other` can access `member`. if (member == other) { - found = Accumulate(found, fn(member) ? ResolutionResult::kFoundNew - : ResolutionResult::kFoundOld); + found = true; + fn(member); } }; for (const DexClass& dex_class : dex_classes_) { @@ -435,20 +434,6 @@ class HierarchyClass final { return found; } - template<typename Fn> - void ForEachOverridingMember(const DexMember& other, Fn fn) { - CHECK(other.IsVirtualMethod()); - ResolutionResult found = ForEachMatchingMember(other, fn); - if (found == ResolutionResult::kFoundOld) { - // No need to explore further. - return; - } else { - for (HierarchyClass* subclass : extended_by_) { - subclass->ForEachOverridingMember(other, fn); - } - } - } - // DexClass entries of this class found across all the provided dex files. std::vector<DexClass> dex_classes_; @@ -1070,12 +1055,7 @@ class HiddenApi final { std::string entry = boot_member.GetApiEntry(); auto it = boot_members.find(entry); CHECK(it != boot_members.end()); - if (it->second.Contains(stub_api_list)) { - return false; // has been marked before - } else { - it->second |= stub_api_list; - return true; // marked for the first time - } + it->second |= stub_api_list; }); if (!resolved) { unresolved.insert(stub_member.GetApiEntry()); diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc index 7ef5b3d2b8..74feb8ac39 100644 --- a/tools/hiddenapi/hiddenapi_test.cc +++ b/tools/hiddenapi/hiddenapi_test.cc @@ -16,6 +16,8 @@ #include <fstream> +#include "android-base/strings.h" + #include "base/unix_file/fd_file.h" #include "base/zip_archive.h" #include "common_runtime_test.h" @@ -41,9 +43,9 @@ class HiddenApiTest : public CommonRuntimeTest { return file_path; } - std::unique_ptr<const DexFile> RunHiddenApi(const ScratchFile& flags_csv, - const std::vector<std::string>& extra_args, - ScratchFile* out_dex) { + std::unique_ptr<const DexFile> RunHiddenapiEncode(const ScratchFile& flags_csv, + const std::vector<std::string>& extra_args, + const ScratchFile& out_dex) { std::string error; ScratchFile in_dex; std::unique_ptr<ZipArchive> jar( @@ -68,18 +70,42 @@ class HiddenApiTest : public CommonRuntimeTest { argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end()); argv_str.push_back("encode"); argv_str.push_back("--input-dex=" + in_dex.GetFilename()); - argv_str.push_back("--output-dex=" + out_dex->GetFilename()); + argv_str.push_back("--output-dex=" + out_dex.GetFilename()); argv_str.push_back("--api-flags=" + flags_csv.GetFilename()); argv_str.push_back("--no-force-assign-all"); int return_code = ExecAndReturnCode(argv_str, &error); if (return_code == 0) { - return OpenDex(*out_dex); + return OpenDex(out_dex); } else { LOG(ERROR) << "HiddenApi binary exited with unexpected return code " << return_code; return nullptr; } } + bool RunHiddenapiList(const ScratchFile& out_flags_csv) { + std::string error; + std::string boot_jar = GetTestDexFileName("HiddenApi"); + std::string stub_jar = GetTestDexFileName("HiddenApiStubs"); + std::string boot_cp = android::base::Join(GetLibCoreDexFileNames(), ":"); + + std::vector<std::string> argv_str; + argv_str.push_back(GetHiddenApiCmd()); + argv_str.push_back("list"); + for (const std::string& core_jar : GetLibCoreDexFileNames()) { + argv_str.push_back("--boot-dex=" + core_jar); + } + argv_str.push_back("--boot-dex=" + boot_jar); + argv_str.push_back("--public-stub-classpath=" + boot_cp + ":" + stub_jar); + argv_str.push_back("--out-api-flags=" + out_flags_csv.GetFilename()); + int return_code = ExecAndReturnCode(argv_str, &error); + if (return_code == 0) { + return true; + } else { + LOG(ERROR) << "HiddenApi binary exited with unexpected return code " << return_code; + return false; + } + } + std::unique_ptr<const DexFile> OpenDex(const ScratchFile& file) { ArtDexFileLoader dex_loader; std::string error_msg; @@ -113,6 +139,31 @@ class HiddenApiTest : public CommonRuntimeTest { return ofs; } + std::map<std::string, std::string> ReadFlagsCsvFile(const ScratchFile& file) { + std::ifstream ifs(file.GetFilename()); + std::map<std::string, std::string> flags; + + for (std::string line; std::getline(ifs, line);) { + std::size_t comma = line.find(","); + if (comma == std::string::npos) { + flags.emplace(line, ""); + } else { + flags.emplace(line.substr(0, comma), line.substr(comma + 1)); + } + } + + return flags; + } + + std::string SafeMapGet(const std::string& key, const std::map<std::string, std::string>& map) { + auto it = map.find(key); + if (it == map.end()) { + LOG(FATAL) << "Key not found: " << key; + UNREACHABLE(); + } + return it->second; + } + const dex::ClassDef& FindClass(const char* desc, const DexFile& dex_file) { const dex::TypeId* type_id = dex_file.FindTypeId(desc); CHECK(type_id != nullptr) << "Could not find class " << desc; @@ -220,7 +271,7 @@ TEST_F(HiddenApiTest, InstanceFieldNoMatch) { << "LMain;->ifield:LBadType1;,greylist" << std::endl << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl << "LMain;->ifield:LBadType3;,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIFieldHiddenFlags(*dex_file)); } @@ -231,7 +282,7 @@ TEST_F(HiddenApiTest, InstanceFieldLightGreylistMatch) { << "LMain;->ifield:I,greylist" << std::endl << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl << "LMain;->ifield:LBadType3;,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIFieldHiddenFlags(*dex_file)); } @@ -242,7 +293,7 @@ TEST_F(HiddenApiTest, InstanceFieldDarkGreylistMatch) { << "LMain;->ifield:LBadType1;,greylist" << std::endl << "LMain;->ifield:I,greylist-max-o" << std::endl << "LMain;->ifield:LBadType3;,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIFieldHiddenFlags(*dex_file)); } @@ -253,7 +304,7 @@ TEST_F(HiddenApiTest, InstanceFieldBlacklistMatch) { << "LMain;->ifield:LBadType1;,greylist" << std::endl << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl << "LMain;->ifield:I,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIFieldHiddenFlags(*dex_file)); } @@ -263,7 +314,7 @@ TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch1) { OpenStream(flags_csv) << "LMain;->ifield:LBadType1;,greylist" << std::endl << "LMain;->ifield:I,blacklist,greylist-max-o" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -272,7 +323,7 @@ TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch2) { OpenStream(flags_csv) << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl << "LMain;->ifield:I,blacklist,greylist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -281,7 +332,7 @@ TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch3) { OpenStream(flags_csv) << "LMain;->ifield:I,greylist,greylist-max-o" << std::endl << "LMain;->ifield:LBadType3;,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -291,7 +342,7 @@ TEST_F(HiddenApiTest, StaticFieldNoMatch) { << "LMain;->sfield:LBadType1;,greylist" << std::endl << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl << "LMain;->sfield:LBadType3;,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSFieldHiddenFlags(*dex_file)); } @@ -302,7 +353,7 @@ TEST_F(HiddenApiTest, StaticFieldLightGreylistMatch) { << "LMain;->sfield:Ljava/lang/Object;,greylist" << std::endl << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl << "LMain;->sfield:LBadType3;,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSFieldHiddenFlags(*dex_file)); } @@ -313,7 +364,7 @@ TEST_F(HiddenApiTest, StaticFieldDarkGreylistMatch) { << "LMain;->sfield:LBadType1;,greylist" << std::endl << "LMain;->sfield:Ljava/lang/Object;,greylist-max-o" << std::endl << "LMain;->sfield:LBadType3;,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSFieldHiddenFlags(*dex_file)); } @@ -324,7 +375,7 @@ TEST_F(HiddenApiTest, StaticFieldBlacklistMatch) { << "LMain;->sfield:LBadType1;,greylist" << std::endl << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl << "LMain;->sfield:Ljava/lang/Object;,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSFieldHiddenFlags(*dex_file)); } @@ -334,7 +385,7 @@ TEST_F(HiddenApiTest, StaticFieldTwoListsMatch1) { OpenStream(flags_csv) << "LMain;->sfield:LBadType1;,greylist" << std::endl << "LMain;->sfield:Ljava/lang/Object;,blacklist,greylist-max-o" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -343,7 +394,7 @@ TEST_F(HiddenApiTest, StaticFieldTwoListsMatch2) { OpenStream(flags_csv) << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl << "LMain;->sfield:Ljava/lang/Object;,blacklist,greylist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -352,7 +403,7 @@ TEST_F(HiddenApiTest, StaticFieldTwoListsMatch3) { OpenStream(flags_csv) << "LMain;->sfield:Ljava/lang/Object;,greylist,greylist-max-o" << std::endl << "LMain;->sfield:LBadType3;,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -362,7 +413,7 @@ TEST_F(HiddenApiTest, InstanceMethodNoMatch) { << "LMain;->imethod(LBadType1;)V,greylist" << std::endl << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIMethodHiddenFlags(*dex_file)); } @@ -373,7 +424,7 @@ TEST_F(HiddenApiTest, InstanceMethodLightGreylistMatch) { << "LMain;->imethod(J)V,greylist" << std::endl << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIMethodHiddenFlags(*dex_file)); } @@ -384,7 +435,7 @@ TEST_F(HiddenApiTest, InstanceMethodDarkGreylistMatch) { << "LMain;->imethod(LBadType1;)V,greylist" << std::endl << "LMain;->imethod(J)V,greylist-max-o" << std::endl << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIMethodHiddenFlags(*dex_file)); } @@ -395,7 +446,7 @@ TEST_F(HiddenApiTest, InstanceMethodBlacklistMatch) { << "LMain;->imethod(LBadType1;)V,greylist" << std::endl << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->imethod(J)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIMethodHiddenFlags(*dex_file)); } @@ -405,7 +456,7 @@ TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch1) { OpenStream(flags_csv) << "LMain;->imethod(LBadType1;)V,greylist" << std::endl << "LMain;->imethod(J)V,blacklist,greylist-max-o" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -414,7 +465,7 @@ TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch2) { OpenStream(flags_csv) << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->imethod(J)V,blacklist,greylist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -423,7 +474,7 @@ TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch3) { OpenStream(flags_csv) << "LMain;->imethod(J)V,greylist,greylist-max-o" << std::endl << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -433,7 +484,7 @@ TEST_F(HiddenApiTest, StaticMethodNoMatch) { << "LMain;->smethod(LBadType1;)V,greylist" << std::endl << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSMethodHiddenFlags(*dex_file)); } @@ -444,7 +495,7 @@ TEST_F(HiddenApiTest, StaticMethodLightGreylistMatch) { << "LMain;->smethod(Ljava/lang/Object;)V,greylist" << std::endl << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSMethodHiddenFlags(*dex_file)); } @@ -455,7 +506,7 @@ TEST_F(HiddenApiTest, StaticMethodDarkGreylistMatch) { << "LMain;->smethod(LBadType1;)V,greylist" << std::endl << "LMain;->smethod(Ljava/lang/Object;)V,greylist-max-o" << std::endl << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSMethodHiddenFlags(*dex_file)); } @@ -466,7 +517,7 @@ TEST_F(HiddenApiTest, StaticMethodBlacklistMatch) { << "LMain;->smethod(LBadType1;)V,greylist" << std::endl << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->smethod(Ljava/lang/Object;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSMethodHiddenFlags(*dex_file)); } @@ -476,7 +527,7 @@ TEST_F(HiddenApiTest, StaticMethodTwoListsMatch1) { OpenStream(flags_csv) << "LMain;->smethod(LBadType1;)V,greylist" << std::endl << "LMain;->smethod(Ljava/lang/Object;)V,blacklist,greylist-max-o" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -485,7 +536,7 @@ TEST_F(HiddenApiTest, StaticMethodTwoListsMatch2) { OpenStream(flags_csv) << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->smethod(Ljava/lang/Object;)V,blacklist,greylist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -494,7 +545,7 @@ TEST_F(HiddenApiTest, StaticMethodTwoListsMatch3) { OpenStream(flags_csv) << "LMain;->smethod(Ljava/lang/Object;)V,greylist,greylist-max-o" << std::endl << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -504,7 +555,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodNoMatch) { << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetINMethodHiddenFlags(*dex_file)); } @@ -515,7 +566,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodLightGreylistMatch) { << "LMain;->inmethod(C)V,greylist" << std::endl << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetINMethodHiddenFlags(*dex_file)); } @@ -526,7 +577,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodDarkGreylistMatch) { << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl << "LMain;->inmethod(C)V,greylist-max-o" << std::endl << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetINMethodHiddenFlags(*dex_file)); } @@ -537,7 +588,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodBlacklistMatch) { << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->inmethod(C)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetINMethodHiddenFlags(*dex_file)); } @@ -547,7 +598,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch1) { OpenStream(flags_csv) << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl << "LMain;->inmethod(C)V,blacklist,greylist-max-o" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -556,7 +607,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch2) { OpenStream(flags_csv) << "LMain;->inmethod(C)V,blacklist,greylist" << std::endl << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -565,7 +616,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch3) { OpenStream(flags_csv) << "LMain;->inmethod(C)V,greylist,greylist-max-o" << std::endl << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -575,7 +626,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodNoMatch) { << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSNMethodHiddenFlags(*dex_file)); } @@ -586,7 +637,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodLightGreylistMatch) { << "LMain;->snmethod(Ljava/lang/Integer;)V,greylist" << std::endl << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSNMethodHiddenFlags(*dex_file)); } @@ -597,7 +648,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodDarkGreylistMatch) { << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl << "LMain;->snmethod(Ljava/lang/Integer;)V,greylist-max-o" << std::endl << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSNMethodHiddenFlags(*dex_file)); } @@ -608,7 +659,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodBlacklistMatch) { << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSNMethodHiddenFlags(*dex_file)); } @@ -618,7 +669,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch1) { OpenStream(flags_csv) << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl << "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist,greylist-max-o" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -627,7 +678,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch2) { OpenStream(flags_csv) << "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist,greylist" << std::endl << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } @@ -636,8 +687,35 @@ TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch3) { OpenStream(flags_csv) << "LMain;->snmethod(Ljava/lang/Integer;)V,greylist,greylist-max-o" << std::endl << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl; - auto dex_file = RunHiddenApi(flags_csv, {}, &dex); + auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } +// The following tests use this class hierarchy: +// +// AbstractPackageClass PublicInterface +// | | +// | ┌----------------┘ +// | | +// PackageClass +// +// Only PublicInterface is in stubs. + +// Test a method declared in PublicInterface and defined in PackageClass. +TEST_F(HiddenApiTest, InterfaceMethodImplemented) { + ScratchFile flags_csv; + ASSERT_TRUE(RunHiddenapiList(flags_csv)); + auto flags = ReadFlagsCsvFile(flags_csv); + ASSERT_EQ(SafeMapGet("LPackageClass;->publicMethod1()V", flags), "whitelist"); +} + +// Test a method declared in PublicInterface, defined in AbstractPackageClass and +// inherited by PackageClass. +TEST_F(HiddenApiTest, InterfaceMethodImplementedInParent) { + ScratchFile flags_csv; + ASSERT_TRUE(RunHiddenapiList(flags_csv)); + auto flags = ReadFlagsCsvFile(flags_csv); + ASSERT_EQ(SafeMapGet("LAbstractPackageClass;->publicMethod2()V", flags), "whitelist"); +} + } // namespace art |