summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author David Brazdil <dbrazdil@google.com> 2019-01-30 16:17:50 +0000
committer David Brazdil <dbrazdil@google.com> 2019-02-01 14:59:57 +0000
commit2da3cbb4af20a64108e474c0bbbe0cc5d3af2aa2 (patch)
tree8cbdf50aab2183c701f1dc7c9ac17d1129fb5238
parent0518af4e87d484b10e785aff9b030b688926cd7f (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.mk3
-rw-r--r--test/HiddenApi/AbstractPackageClass.java19
-rw-r--r--test/HiddenApi/PackageClass.java19
-rw-r--r--test/HiddenApi/PublicInterface.java20
-rw-r--r--test/HiddenApiStubs/HiddenApi19
-rw-r--r--test/HiddenApiStubs/PublicInterface.java20
-rw-r--r--tools/hiddenapi/hiddenapi.cc120
-rw-r--r--tools/hiddenapi/hiddenapi_test.cc172
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