summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--build/Android.gtest.mk8
-rw-r--r--dex2oat/linker/oat_writer.cc14
-rw-r--r--runtime/dex/art_dex_file_loader.cc15
-rw-r--r--runtime/dex/art_dex_file_loader.h2
-rw-r--r--runtime/dex/dex_file.h11
-rw-r--r--runtime/dex/dex_file_loader.cc2
-rw-r--r--runtime/dex/dex_file_loader.h2
-rw-r--r--runtime/dex/dex_hidden_access_flags.h110
-rw-r--r--runtime/leb128.h2
-rw-r--r--runtime/modifiers.h8
-rw-r--r--runtime/parsed_options.cc3
-rw-r--r--runtime/runtime.cc4
-rw-r--r--runtime/runtime.h2
-rw-r--r--runtime/runtime_options.def1
-rw-r--r--test/HiddenApi/Main.java26
-rw-r--r--tools/hiddenapi/Android.bp62
-rw-r--r--tools/hiddenapi/README.md54
-rw-r--r--tools/hiddenapi/hiddenapi.cc408
-rw-r--r--tools/hiddenapi/hiddenapi_test.cc600
20 files changed, 1324 insertions, 11 deletions
diff --git a/Android.bp b/Android.bp
index 197860694b..caf4f9a325 100644
--- a/Android.bp
+++ b/Android.bp
@@ -47,6 +47,7 @@ subdirs = [
"tools/breakpoint-logger",
"tools/cpp-define-generator",
"tools/dmtracedump",
+ "tools/hiddenapi",
"tools/titrace",
"tools/wrapagentproperties",
]
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 45f4e2d4d8..4f5df03c19 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -36,6 +36,7 @@ GTEST_DEX_DIRECTORIES := \
ForClassLoaderD \
ExceptionHandle \
GetMethodSignature \
+ HiddenApi \
ImageLayoutA \
ImageLayoutB \
IMTA \
@@ -113,6 +114,7 @@ ART_GTEST_dexlayout_test_DEX_DEPS := ManyMethods
ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps
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_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods
ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB
ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
@@ -266,6 +268,11 @@ ART_GTEST_patchoat_test_TARGET_DEPS := \
ART_GTEST_profile_assistant_test_HOST_DEPS := profmand-host
ART_GTEST_profile_assistant_test_TARGET_DEPS := profmand-target
+ART_GTEST_hiddenapi_test_HOST_DEPS := \
+ $(HOST_CORE_IMAGE_DEFAULT_64) \
+ $(HOST_CORE_IMAGE_DEFAULT_32) \
+ hiddenapid-host
+
# The path for which all the source files are relative, not actually the current directory.
LOCAL_PATH := art
@@ -279,6 +286,7 @@ ART_TEST_MODULES := \
art_dexlayout_tests \
art_dexlist_tests \
art_dexoptanalyzer_tests \
+ art_hiddenapi_tests \
art_imgdiag_tests \
art_oatdump_tests \
art_patchoat_tests \
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 28ded6ef85..44493283db 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -3434,6 +3434,11 @@ bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex
}
bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file) {
+ // Open dex files and write them into `out`.
+ // Note that we only verify dex files which do not belong to the boot class path.
+ // This is because those have been processed by `hiddenapi` and would not pass
+ // some of the checks. No guarantees are lost, however, as `hiddenapi` verifies
+ // the dex files prior to processing.
TimingLogger::ScopedTiming split("Dex Layout", timings_);
std::string error_msg;
std::string location(oat_dex_file->GetLocation());
@@ -3450,7 +3455,7 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil
dex_file = dex_file_loader.Open(location,
zip_entry->GetCrc32(),
std::move(mem_map),
- /* verify */ true,
+ /* verify */ !compiling_boot_image_,
/* verify_checksum */ true,
&error_msg);
} else if (oat_dex_file->source_.IsRawFile()) {
@@ -3460,8 +3465,11 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil
PLOG(ERROR) << "Failed to dup dex file descriptor (" << raw_file->Fd() << ") at " << location;
return false;
}
- dex_file = dex_file_loader.OpenDex(
- dup_fd, location, /* verify */ true, /* verify_checksum */ true, &error_msg);
+ dex_file = dex_file_loader.OpenDex(dup_fd, location,
+ /* verify */ !compiling_boot_image_,
+ /* verify_checksum */ true,
+ /* mmap_shared */ false,
+ &error_msg);
} else {
// The source data is a vdex file.
CHECK(oat_dex_file->source_.IsRawData())
diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc
index 282b282707..dee736ecff 100644
--- a/runtime/dex/art_dex_file_loader.cc
+++ b/runtime/dex/art_dex_file_loader.cc
@@ -127,8 +127,12 @@ bool ArtDexFileLoader::GetMultiDexChecksums(const char* filename,
return true;
}
if (IsMagicValid(magic)) {
- std::unique_ptr<const DexFile> dex_file(
- OpenFile(fd.Release(), filename, false, false, error_msg));
+ std::unique_ptr<const DexFile> dex_file(OpenFile(fd.Release(),
+ filename,
+ /* verify */ false,
+ /* verify_checksum */ false,
+ /* mmap_shared */ false,
+ error_msg));
if (dex_file == nullptr) {
return false;
}
@@ -211,6 +215,7 @@ bool ArtDexFileLoader::Open(const char* filename,
location,
verify,
verify_checksum,
+ /* mmap_shared */ false,
error_msg));
if (dex_file.get() != nullptr) {
dex_files->push_back(std::move(dex_file));
@@ -227,9 +232,10 @@ std::unique_ptr<const DexFile> ArtDexFileLoader::OpenDex(int fd,
const std::string& location,
bool verify,
bool verify_checksum,
+ bool mmap_shared,
std::string* error_msg) const {
ScopedTrace trace("Open dex file " + std::string(location));
- return OpenFile(fd, location, verify, verify_checksum, error_msg);
+ return OpenFile(fd, location, verify, verify_checksum, mmap_shared, error_msg);
}
bool ArtDexFileLoader::OpenZip(int fd,
@@ -253,6 +259,7 @@ std::unique_ptr<const DexFile> ArtDexFileLoader::OpenFile(int fd,
const std::string& location,
bool verify,
bool verify_checksum,
+ bool mmap_shared,
std::string* error_msg) const {
ScopedTrace trace(std::string("Open dex file ") + std::string(location));
CHECK(!location.empty());
@@ -273,7 +280,7 @@ std::unique_ptr<const DexFile> ArtDexFileLoader::OpenFile(int fd,
size_t length = sbuf.st_size;
map.reset(MemMap::MapFile(length,
PROT_READ,
- MAP_PRIVATE,
+ mmap_shared ? MAP_SHARED : MAP_PRIVATE,
fd,
0,
/*low_4gb*/false,
diff --git a/runtime/dex/art_dex_file_loader.h b/runtime/dex/art_dex_file_loader.h
index a6191d9f54..8c12bf3137 100644
--- a/runtime/dex/art_dex_file_loader.h
+++ b/runtime/dex/art_dex_file_loader.h
@@ -83,6 +83,7 @@ class ArtDexFileLoader : public DexFileLoader {
const std::string& location,
bool verify,
bool verify_checksum,
+ bool mmap_shared,
std::string* error_msg) const OVERRIDE;
// Opens dex files from within a .jar, .zip, or .apk file
@@ -98,6 +99,7 @@ class ArtDexFileLoader : public DexFileLoader {
const std::string& location,
bool verify,
bool verify_checksum,
+ bool mmap_shared,
std::string* error_msg) const OVERRIDE;
// Open all classesXXX.dex files from a zip archive.
diff --git a/runtime/dex/dex_file.h b/runtime/dex/dex_file.h
index 183d84e15d..695527605d 100644
--- a/runtime/dex/dex_file.h
+++ b/runtime/dex/dex_file.h
@@ -27,6 +27,7 @@
#include "base/macros.h"
#include "base/value_object.h"
#include "dex_file_types.h"
+#include "dex_hidden_access_flags.h"
#include "dex_instruction_iterator.h"
#include "globals.h"
#include "jni.h"
@@ -1252,10 +1253,16 @@ class ClassDataItemIterator {
}
}
uint32_t GetFieldAccessFlags() const {
- return GetRawMemberAccessFlags() & kAccValidFieldFlags;
+ return GetMemberAccessFlags() & kAccValidFieldFlags;
}
uint32_t GetMethodAccessFlags() const {
- return GetRawMemberAccessFlags() & kAccValidMethodFlags;
+ return GetMemberAccessFlags() & kAccValidMethodFlags;
+ }
+ uint32_t GetMemberAccessFlags() const {
+ return DexHiddenAccessFlags::RemoveHiddenFlags(GetRawMemberAccessFlags());
+ }
+ DexHiddenAccessFlags::ApiList DecodeHiddenAccessFlags() const {
+ return DexHiddenAccessFlags::Decode(GetRawMemberAccessFlags());
}
bool MemberIsNative() const {
return GetRawMemberAccessFlags() & kAccNative;
diff --git a/runtime/dex/dex_file_loader.cc b/runtime/dex/dex_file_loader.cc
index 10aef56125..6ae1d73fdd 100644
--- a/runtime/dex/dex_file_loader.cc
+++ b/runtime/dex/dex_file_loader.cc
@@ -132,6 +132,7 @@ std::unique_ptr<const DexFile> DexFileLoader::OpenDex(
const std::string& location ATTRIBUTE_UNUSED,
bool verify ATTRIBUTE_UNUSED,
bool verify_checksum ATTRIBUTE_UNUSED,
+ bool mmap_shared ATTRIBUTE_UNUSED,
std::string* error_msg) const {
*error_msg = "UNIMPLEMENTED";
return nullptr;
@@ -153,6 +154,7 @@ std::unique_ptr<const DexFile> DexFileLoader::OpenFile(
const std::string& location ATTRIBUTE_UNUSED,
bool verify ATTRIBUTE_UNUSED,
bool verify_checksum ATTRIBUTE_UNUSED,
+ bool mmap_shared ATTRIBUTE_UNUSED,
std::string* error_msg) const {
*error_msg = "UNIMPLEMENTED";
return nullptr;
diff --git a/runtime/dex/dex_file_loader.h b/runtime/dex/dex_file_loader.h
index 6f1afd636f..4e45fb03b8 100644
--- a/runtime/dex/dex_file_loader.h
+++ b/runtime/dex/dex_file_loader.h
@@ -97,6 +97,7 @@ class DexFileLoader {
const std::string& location,
bool verify,
bool verify_checksum,
+ bool mmap_shared,
std::string* error_msg) const;
// Opens dex files from within a .jar, .zip, or .apk file
@@ -182,6 +183,7 @@ class DexFileLoader {
const std::string& location,
bool verify,
bool verify_checksum,
+ bool mmap_shared,
std::string* error_msg) const;
// Open all classesXXX.dex files from a zip archive.
diff --git a/runtime/dex/dex_hidden_access_flags.h b/runtime/dex/dex_hidden_access_flags.h
new file mode 100644
index 0000000000..16fae86b24
--- /dev/null
+++ b/runtime/dex/dex_hidden_access_flags.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ART_RUNTIME_DEX_DEX_HIDDEN_ACCESS_FLAGS_H_
+#define ART_RUNTIME_DEX_DEX_HIDDEN_ACCESS_FLAGS_H_
+
+#include "base/bit_utils.h"
+#include "modifiers.h"
+
+namespace art {
+
+/* This class is used for encoding and decoding access flags of DexFile members
+ * from the boot class path. These access flags might contain additional two bits
+ * of information on whether the given class member should be hidden from apps.
+ *
+ * First bit is encoded as inversion of visibility flags (public/private/protected).
+ * At most one can be set for any given class member. If two or three are set,
+ * this is interpreted as the first bit being set and actual visibility flags
+ * being the complement of the encoded flags.
+ *
+ * Second bit is either encoded as bit 5 for fields and non-native methods, where
+ * it carries no other meaning. If a method is native, bit 9 is used.
+ *
+ * Bits were selected so that they never increase the length of unsigned LEB-128
+ * encoding of the access flags.
+ */
+class DexHiddenAccessFlags {
+ public:
+ enum ApiList {
+ kWhitelist = 0,
+ kLightGreylist,
+ kDarkGreylist,
+ kBlacklist,
+ };
+
+ static ALWAYS_INLINE ApiList Decode(uint32_t access_flags) {
+ DexHiddenAccessFlags flags(access_flags);
+ uint32_t int_value = (flags.IsFirstBitSet() ? 1 : 0) + (flags.IsSecondBitSet() ? 2 : 0);
+ return static_cast<ApiList>(int_value);
+ }
+
+ static ALWAYS_INLINE uint32_t RemoveHiddenFlags(uint32_t access_flags) {
+ DexHiddenAccessFlags flags(access_flags);
+ flags.SetFirstBit(false);
+ flags.SetSecondBit(false);
+ return flags.GetEncoding();
+ }
+
+ static ALWAYS_INLINE uint32_t Encode(uint32_t access_flags, ApiList value) {
+ DexHiddenAccessFlags flags(access_flags);
+ uint32_t int_value = static_cast<uint32_t>(value);
+ flags.SetFirstBit((int_value & 1) != 0);
+ flags.SetSecondBit((int_value & 2) != 0);
+ return flags.GetEncoding();
+ }
+
+ private:
+ explicit DexHiddenAccessFlags(uint32_t access_flags) : access_flags_(access_flags) {}
+
+ ALWAYS_INLINE uint32_t GetSecondFlag() {
+ return ((access_flags_ & kAccNative) != 0) ? kAccDexHiddenBitNative : kAccDexHiddenBit;
+ }
+
+ ALWAYS_INLINE bool IsFirstBitSet() {
+ static_assert(IsPowerOfTwo(0u), "Following statement checks if *at most* one bit is set");
+ return !IsPowerOfTwo(access_flags_ & kAccVisibilityFlags);
+ }
+
+ ALWAYS_INLINE void SetFirstBit(bool value) {
+ if (IsFirstBitSet() != value) {
+ access_flags_ ^= kAccVisibilityFlags;
+ }
+ }
+
+ ALWAYS_INLINE bool IsSecondBitSet() {
+ return (access_flags_ & GetSecondFlag()) != 0;
+ }
+
+ ALWAYS_INLINE void SetSecondBit(bool value) {
+ if (value) {
+ access_flags_ |= GetSecondFlag();
+ } else {
+ access_flags_ &= ~GetSecondFlag();
+ }
+ }
+
+ ALWAYS_INLINE uint32_t GetEncoding() const {
+ return access_flags_;
+ }
+
+ uint32_t access_flags_;
+};
+
+} // namespace art
+
+
+#endif // ART_RUNTIME_DEX_DEX_HIDDEN_ACCESS_FLAGS_H_
diff --git a/runtime/leb128.h b/runtime/leb128.h
index 2bfed7f539..9fb09d8fc2 100644
--- a/runtime/leb128.h
+++ b/runtime/leb128.h
@@ -241,7 +241,7 @@ static inline void EncodeUnsignedLeb128(Vector* dest, uint32_t value) {
static inline void UpdateUnsignedLeb128(uint8_t* dest, uint32_t value) {
const uint8_t* old_end = dest;
uint32_t old_value = DecodeUnsignedLeb128(&old_end);
- DCHECK_LE(value, old_value);
+ DCHECK_LE(UnsignedLeb128Size(value), UnsignedLeb128Size(old_value));
for (uint8_t* end = EncodeUnsignedLeb128(dest, value); end < old_end; end++) {
// Use longer encoding than necessary to fill the allocated space.
end[-1] |= 0x80;
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index d7d647b8fd..a72f9daad6 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -42,6 +42,12 @@ static constexpr uint32_t kAccEnum = 0x4000; // class, field, ic (1.5)
static constexpr uint32_t kAccJavaFlagsMask = 0xffff; // bits set from Java sources (low 16)
+// The following flags are used to insert hidden API access flags into boot
+// class path dex files. They are decoded by DexFile::ClassDataItemIterator and
+// removed from the access flags before used by the runtime.
+static constexpr uint32_t kAccDexHiddenBit = 0x00000020; // field, method (not native)
+static constexpr uint32_t kAccDexHiddenBitNative = 0x00000200; // method (native)
+
static constexpr uint32_t kAccConstructor = 0x00010000; // method (dex only) <(cl)init>
static constexpr uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only)
static constexpr uint32_t kAccClassIsProxy = 0x00040000; // class (dex only)
@@ -127,6 +133,8 @@ static constexpr uint32_t kAccValidClassFlags = kAccPublic | kAccFinal | kAccSup
static constexpr uint32_t kAccValidInterfaceFlags = kAccPublic | kAccInterface |
kAccAbstract | kAccSynthetic | kAccAnnotation;
+static constexpr uint32_t kAccVisibilityFlags = kAccPublic | kAccPrivate | kAccProtected;
+
} // namespace art
#endif // ART_RUNTIME_MODIFIERS_H_
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 3ac3d03e90..2f60162c77 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -327,6 +327,9 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
.WithType<bool>()
.WithValueMap({{"false", false}, {"true", true}})
.IntoKey(M::SlowDebug)
+ .Define("-Xtarget-sdk-version:_")
+ .WithType<int>()
+ .IntoKey(M::TargetSdkVersion)
.Ignore({
"-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
"-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 377e0a3fca..007d361976 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -250,7 +250,7 @@ Runtime::Runtime()
preinitialization_transactions_(),
verify_(verifier::VerifyMode::kNone),
allow_dex_file_fallback_(true),
- target_sdk_version_(0),
+ target_sdk_version_(kUnsetSdkVersion),
implicit_null_checks_(false),
implicit_so_checks_(false),
implicit_suspend_checks_(false),
@@ -1166,6 +1166,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
verify_ = runtime_options.GetOrDefault(Opt::Verify);
allow_dex_file_fallback_ = !runtime_options.Exists(Opt::NoDexFileFallback);
+ target_sdk_version_ = runtime_options.GetOrDefault(Opt::TargetSdkVersion);
+
no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain);
force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 3e055c3f84..6d2887cc42 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -709,6 +709,8 @@ class Runtime {
return jdwp_provider_;
}
+ static constexpr int32_t kUnsetSdkVersion = 0u;
+
private:
static void InitPlatformSignalHandlers();
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 1dd3de5039..3996989920 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -118,6 +118,7 @@ RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
ImageCompilerOptions) // -Ximage-compiler-option ...
RUNTIME_OPTIONS_KEY (verifier::VerifyMode, \
Verify, verifier::VerifyMode::kEnable)
+RUNTIME_OPTIONS_KEY (int, TargetSdkVersion, Runtime::kUnsetSdkVersion)
RUNTIME_OPTIONS_KEY (std::string, NativeBridge)
RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 10)
RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback)
diff --git a/test/HiddenApi/Main.java b/test/HiddenApi/Main.java
new file mode 100644
index 0000000000..187dd6e599
--- /dev/null
+++ b/test/HiddenApi/Main.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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 Main {
+ public int ifield;
+ private static Object sfield;
+
+ void imethod(long x) {}
+ public static void smethod(Object x) {}
+
+ public native void inmethod(char x);
+ protected native static void snmethod(Integer x);
+}
diff --git a/tools/hiddenapi/Android.bp b/tools/hiddenapi/Android.bp
new file mode 100644
index 0000000000..ccab7f88b4
--- /dev/null
+++ b/tools/hiddenapi/Android.bp
@@ -0,0 +1,62 @@
+//
+// Copyright (C) 2017 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.
+//
+
+cc_defaults {
+ name: "hiddenapi-defaults",
+ host_supported: true,
+ device_supported: false,
+ defaults: ["art_defaults"],
+ srcs: [
+ "hiddenapi.cc",
+ ],
+
+ target: {
+ android: {
+ compile_multilib: "prefer32",
+ },
+ },
+
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+art_cc_binary {
+ name: "hiddenapi",
+ defaults: ["hiddenapi-defaults"],
+ shared_libs: [
+ "libart",
+ ],
+}
+
+art_cc_binary {
+ name: "hiddenapid",
+ defaults: [
+ "art_debug_defaults",
+ "hiddenapi-defaults",
+ ],
+ shared_libs: [
+ "libartd",
+ ],
+}
+
+art_cc_test {
+ name: "art_hiddenapi_tests",
+ defaults: [
+ "art_gtest_defaults",
+ ],
+ srcs: ["hiddenapi_test.cc"],
+}
diff --git a/tools/hiddenapi/README.md b/tools/hiddenapi/README.md
new file mode 100644
index 0000000000..cad12126dd
--- /dev/null
+++ b/tools/hiddenapi/README.md
@@ -0,0 +1,54 @@
+HiddenApi
+=========
+
+This tool iterates over all class members inside given DEX files and modifies
+their access flags if their signatures appear on one of two lists - greylist and
+blacklist - provided as text file inputs. These access flags denote to the
+runtime that the marked methods/fields should be treated as internal APIs with
+access restricted only to platform code. Methods/fields not mentioned on the two
+lists are assumed to be on a whitelist and left accessible by all code.
+
+API signatures
+==============
+
+The methods/fields to be marked are specified in two text files (greylist,
+blacklist) provided an input. Only one signature per line is allowed.
+
+Types are expected in their DEX format - class descriptors are to be provided in
+"slash" form, e.g. "Ljava/lang/Object;", primitive types in their shorty form,
+e.g. "I" for "int", and a "[" prefix denotes an array type. Lists of types do
+not use any separators, e.g. "ILxyz;F" for "int, xyz, float".
+
+Methods are encoded as:
+ `class_descriptor->method_name(parameter_types)return_type`
+
+Fields are encoded as:
+ `class_descriptor->field_name:field_type`
+
+Bit encoding
+============
+
+Two bits of information are encoded in the DEX access flags. These are encoded
+as unsigned LEB128 values in DEX and so as to not increase the size of the DEX,
+different modifiers were chosen for different kinds of methods/fields.
+
+First bit is encoded as the inversion of visibility access flags (bits 2:0).
+At most one of these flags can be set at any given time. Inverting these bits
+therefore produces a value where at least two bits are set and there is never
+any loss of information.
+
+Second bit is encoded differently for each given type of class member as there
+is no single unused bit such that setting it would not increase the size of the
+LEB128 encoding. The following bits are used:
+
+ * bit 5 for fields as it carries no other meaning
+ * bit 5 for non-native methods, as `synchronized` can only be set on native
+ methods (the Java `synchronized` modifier is bit 17)
+ * bit 9 for native methods, as it carries no meaning and bit 8 (`native`) will
+ make the LEB128 encoding at least two bytes long
+
+Two following bit encoding is used to denote the membership of a method/field:
+
+ * whitelist: `false`, `false`
+ * greylist: `true`, `false`
+ * blacklist: `true`, `true`
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
new file mode 100644
index 0000000000..fe72bb0231
--- /dev/null
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <fstream>
+#include <iostream>
+#include <unordered_set>
+
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+
+#include "base/unix_file/fd_file.h"
+#include "dex/art_dex_file_loader.h"
+#include "dex/dex_file-inl.h"
+#include "dex/dex_hidden_access_flags.h"
+#include "mem_map.h"
+#include "os.h"
+
+namespace art {
+
+static int original_argc;
+static char** original_argv;
+
+static std::string CommandLine() {
+ std::vector<std::string> command;
+ for (int i = 0; i < original_argc; ++i) {
+ command.push_back(original_argv[i]);
+ }
+ return android::base::Join(command, ' ');
+}
+
+static void UsageErrorV(const char* fmt, va_list ap) {
+ std::string error;
+ android::base::StringAppendV(&error, fmt, ap);
+ LOG(ERROR) << error;
+}
+
+static void UsageError(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ UsageErrorV(fmt, ap);
+ va_end(ap);
+}
+
+NO_RETURN static void Usage(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ UsageErrorV(fmt, ap);
+ va_end(ap);
+
+ UsageError("Command: %s", CommandLine().c_str());
+ UsageError("Usage: hiddenapi [options]...");
+ UsageError("");
+ UsageError(" --dex=<filename>: specify dex file whose members' access flags are to be set.");
+ UsageError(" At least one --dex parameter must be specified.");
+ UsageError("");
+ UsageError(" --light-greylist=<filename>:");
+ UsageError(" --dark-greylist=<filename>:");
+ UsageError(" --blacklist=<filename>: text files with signatures of methods/fields to be marked");
+ UsageError(" greylisted/blacklisted respectively. At least one list must be provided.");
+ UsageError("");
+ UsageError(" --print-hidden-api: dump a list of marked methods/fields to the standard output.");
+ UsageError(" There is no indication which API category they belong to.");
+ UsageError("");
+
+ exit(EXIT_FAILURE);
+}
+
+class DexClass {
+ public:
+ DexClass(const DexFile& dex_file, uint32_t idx)
+ : dex_file_(dex_file), class_def_(dex_file.GetClassDef(idx)) {}
+
+ const DexFile& GetDexFile() const { return dex_file_; }
+
+ const dex::TypeIndex GetClassIndex() const { return class_def_.class_idx_; }
+
+ const uint8_t* GetData() const { return dex_file_.GetClassData(class_def_); }
+
+ const char* GetDescriptor() const { return dex_file_.GetClassDescriptor(class_def_); }
+
+ private:
+ const DexFile& dex_file_;
+ const DexFile::ClassDef& class_def_;
+};
+
+class DexMember {
+ public:
+ DexMember(const DexClass& klass, const ClassDataItemIterator& it)
+ : klass_(klass), it_(it) {
+ DCHECK_EQ(it_.IsAtMethod() ? GetMethodId().class_idx_ : GetFieldId().class_idx_,
+ klass_.GetClassIndex());
+ }
+
+ // Sets hidden bits in access flags and writes them back into the DEX in memory.
+ // Note that this will not update the cached data of ClassDataItemIterator
+ // until it iterates over this item again and therefore will fail a CHECK if
+ // it is called multiple times on the same DexMember.
+ void SetHidden(DexHiddenAccessFlags::ApiList value) {
+ const uint32_t old_flags = it_.GetRawMemberAccessFlags();
+ const uint32_t new_flags = DexHiddenAccessFlags::Encode(old_flags, value);
+ CHECK_EQ(UnsignedLeb128Size(new_flags), UnsignedLeb128Size(old_flags));
+
+ // Locate the LEB128-encoded access flags in class data.
+ // `ptr` initially points to the next ClassData item. We iterate backwards
+ // until we hit the terminating byte of the previous Leb128 value.
+ const uint8_t* ptr = it_.DataPointer();
+ if (it_.IsAtMethod()) {
+ ptr = ReverseSearchUnsignedLeb128(ptr, it_.GetMethodCodeItemOffset());
+ }
+ ptr = ReverseSearchUnsignedLeb128(ptr, old_flags);
+
+ // Overwrite the access flags.
+ UpdateUnsignedLeb128(const_cast<uint8_t*>(ptr), new_flags);
+ }
+
+ // Returns true if this member's API entry is in `list`.
+ bool IsOnApiList(const std::unordered_set<std::string>& list) const {
+ return list.find(GetApiEntry()) != list.end();
+ }
+
+ // Constructs a string with a unique signature of this class member.
+ std::string GetApiEntry() const {
+ std::stringstream ss;
+ ss << klass_.GetDescriptor() << "->";
+ if (it_.IsAtMethod()) {
+ const DexFile::MethodId& mid = GetMethodId();
+ ss << klass_.GetDexFile().GetMethodName(mid)
+ << klass_.GetDexFile().GetMethodSignature(mid).ToString();
+ } else {
+ const DexFile::FieldId& fid = GetFieldId();
+ ss << klass_.GetDexFile().GetFieldName(fid) << ":"
+ << klass_.GetDexFile().GetFieldTypeDescriptor(fid);
+ }
+ return ss.str();
+ }
+
+ private:
+ inline const DexFile::MethodId& GetMethodId() const {
+ DCHECK(it_.IsAtMethod());
+ return klass_.GetDexFile().GetMethodId(it_.GetMemberIndex());
+ }
+
+ inline const DexFile::FieldId& GetFieldId() const {
+ DCHECK(!it_.IsAtMethod());
+ return klass_.GetDexFile().GetFieldId(it_.GetMemberIndex());
+ }
+
+ static inline bool IsLeb128Terminator(const uint8_t* ptr) {
+ return *ptr <= 0x7f;
+ }
+
+ // Returns the first byte of a Leb128 value assuming that:
+ // (1) `end_ptr` points to the first byte after the Leb128 value, and
+ // (2) there is another Leb128 value before this one.
+ // The function will fail after reading 5 bytes (the longest supported Leb128
+ // encoding) to protect against situations when (2) is not satisfied.
+ // When a Leb128 value is discovered, it is decoded and CHECKed against `value`.
+ static const uint8_t* ReverseSearchUnsignedLeb128(const uint8_t* end_ptr, uint32_t expected) {
+ const uint8_t* ptr = end_ptr;
+
+ // Move one byte back, check that this is the terminating byte.
+ ptr--;
+ CHECK(IsLeb128Terminator(ptr));
+
+ // Keep moving back while the previous byte is not a terminating byte.
+ // Fail after reading five bytes in case there isn't another Leb128 value
+ // before this one.
+ while (!IsLeb128Terminator(ptr - 1)) {
+ ptr--;
+ CHECK_LE((size_t) (end_ptr - ptr), 5u);
+ }
+
+ // Check that the decoded value matches the `expected` value.
+ const uint8_t* tmp_ptr = ptr;
+ CHECK_EQ(DecodeUnsignedLeb128(&tmp_ptr), expected);
+
+ return ptr;
+ }
+
+ const DexClass& klass_;
+ const ClassDataItemIterator& it_;
+};
+
+class HiddenApi FINAL {
+ public:
+ HiddenApi() : print_hidden_api_(false) {}
+
+ void ParseArgs(int argc, char** argv) {
+ original_argc = argc;
+ original_argv = argv;
+
+ android::base::InitLogging(argv);
+
+ // Skip over the command name.
+ argv++;
+ argc--;
+
+ if (argc == 0) {
+ Usage("No arguments specified");
+ }
+
+ for (int i = 0; i < argc; ++i) {
+ const StringPiece option(argv[i]);
+ const bool log_options = false;
+ if (log_options) {
+ LOG(INFO) << "hiddenapi: option[" << i << "]=" << argv[i];
+ }
+ if (option == "--print-hidden-api") {
+ print_hidden_api_ = true;
+ } else if (option.starts_with("--dex=")) {
+ dex_paths_.push_back(option.substr(strlen("--dex=")).ToString());
+ } else if (option.starts_with("--light-greylist=")) {
+ light_greylist_path_ = option.substr(strlen("--light-greylist=")).ToString();
+ } else if (option.starts_with("--dark-greylist=")) {
+ dark_greylist_path_ = option.substr(strlen("--dark-greylist=")).ToString();
+ } else if (option.starts_with("--blacklist=")) {
+ blacklist_path_ = option.substr(strlen("--blacklist=")).ToString();
+ } else {
+ Usage("Unknown argument '%s'", option.data());
+ }
+ }
+ }
+
+ bool ProcessDexFiles() {
+ if (dex_paths_.empty()) {
+ Usage("No DEX files specified");
+ }
+
+ if (light_greylist_path_.empty() && dark_greylist_path_.empty() && blacklist_path_.empty()) {
+ Usage("No API file specified");
+ }
+
+ if (!light_greylist_path_.empty() && !OpenApiFile(light_greylist_path_, &light_greylist_)) {
+ return false;
+ }
+
+ if (!dark_greylist_path_.empty() && !OpenApiFile(dark_greylist_path_, &dark_greylist_)) {
+ return false;
+ }
+
+ if (!blacklist_path_.empty() && !OpenApiFile(blacklist_path_, &blacklist_)) {
+ return false;
+ }
+
+ MemMap::Init();
+ if (!OpenDexFiles()) {
+ return false;
+ }
+
+ DCHECK(!dex_files_.empty());
+ for (auto& dex_file : dex_files_) {
+ CategorizeAllClasses(*dex_file.get());
+ }
+
+ UpdateDexChecksums();
+ return true;
+ }
+
+ private:
+ bool OpenApiFile(const std::string& path, std::unordered_set<std::string>* list) {
+ DCHECK(list->empty());
+ DCHECK(!path.empty());
+
+ std::ifstream api_file(path, std::ifstream::in);
+ if (api_file.fail()) {
+ LOG(ERROR) << "Unable to open file '" << path << "' " << strerror(errno);
+ return false;
+ }
+
+ for (std::string line; std::getline(api_file, line);) {
+ list->insert(line);
+ }
+
+ api_file.close();
+ return true;
+ }
+
+ bool OpenDexFiles() {
+ ArtDexFileLoader dex_loader;
+ DCHECK(dex_files_.empty());
+
+ for (const std::string& filename : dex_paths_) {
+ std::string error_msg;
+
+ File fd(filename.c_str(), O_RDWR, /* check_usage */ false);
+ if (fd.Fd() == -1) {
+ LOG(ERROR) << "Unable to open file '" << filename << "': " << strerror(errno);
+ return false;
+ }
+
+ // Memory-map the dex file with MAP_SHARED flag so that changes in memory
+ // propagate to the underlying file. We run dex file verification as if
+ // the dex file was not in boot claass path to check basic assumptions,
+ // such as that at most one of public/private/protected flag is set.
+ // We do those checks here and skip them when loading the processed file
+ // into boot class path.
+ std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(fd.Release(),
+ /* location */ filename,
+ /* verify */ true,
+ /* verify_checksum */ true,
+ /* mmap_shared */ true,
+ &error_msg));
+ if (dex_file.get() == nullptr) {
+ LOG(ERROR) << "Open failed for '" << filename << "' " << error_msg;
+ return false;
+ }
+
+ if (!dex_file->IsStandardDexFile()) {
+ LOG(ERROR) << "Expected a standard dex file '" << filename << "'";
+ return false;
+ }
+
+ // Change the protection of the memory mapping to read-write.
+ if (!dex_file->EnableWrite()) {
+ LOG(ERROR) << "Failed to enable write permission for '" << filename << "'";
+ return false;
+ }
+
+ dex_files_.push_back(std::move(dex_file));
+ }
+ return true;
+ }
+
+ void CategorizeAllClasses(const DexFile& dex_file) {
+ for (uint32_t class_idx = 0; class_idx < dex_file.NumClassDefs(); ++class_idx) {
+ DexClass klass(dex_file, class_idx);
+ const uint8_t* klass_data = klass.GetData();
+ if (klass_data == nullptr) {
+ continue;
+ }
+
+ for (ClassDataItemIterator it(klass.GetDexFile(), klass_data); it.HasNext(); it.Next()) {
+ DexMember member(klass, it);
+
+ // Catagorize member and overwrite its access flags.
+ // Note that if a member appears on multiple API lists, it will be categorized
+ // as the strictest.
+ bool is_hidden = true;
+ if (member.IsOnApiList(blacklist_)) {
+ member.SetHidden(DexHiddenAccessFlags::kBlacklist);
+ } else if (member.IsOnApiList(dark_greylist_)) {
+ member.SetHidden(DexHiddenAccessFlags::kDarkGreylist);
+ } else if (member.IsOnApiList(light_greylist_)) {
+ member.SetHidden(DexHiddenAccessFlags::kLightGreylist);
+ } else {
+ member.SetHidden(DexHiddenAccessFlags::kWhitelist);
+ is_hidden = false;
+ }
+
+ if (print_hidden_api_ && is_hidden) {
+ std::cout << member.GetApiEntry() << std::endl;
+ }
+ }
+ }
+ }
+
+ void UpdateDexChecksums() {
+ for (auto& dex_file : dex_files_) {
+ // Obtain a writeable pointer to the dex header.
+ DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader());
+ // Recalculate checksum and overwrite the value in the header.
+ header->checksum_ = dex_file->CalculateChecksum();
+ }
+ }
+
+ // Print signatures of APIs which have been grey-/blacklisted.
+ bool print_hidden_api_;
+
+ // Paths to DEX files which should be processed.
+ std::vector<std::string> dex_paths_;
+
+ // Paths to text files which contain the lists of API members.
+ std::string light_greylist_path_;
+ std::string dark_greylist_path_;
+ std::string blacklist_path_;
+
+ // Opened DEX files. Note that these are opened as `const` but eventually will be written into.
+ std::vector<std::unique_ptr<const DexFile>> dex_files_;
+
+ // Signatures of DEX members loaded from `light_greylist_path_`, `dark_greylist_path_`,
+ // `blacklist_path_`.
+ std::unordered_set<std::string> light_greylist_;
+ std::unordered_set<std::string> dark_greylist_;
+ std::unordered_set<std::string> blacklist_;
+};
+
+} // namespace art
+
+int main(int argc, char** argv) {
+ art::HiddenApi hiddenapi;
+
+ // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
+ hiddenapi.ParseArgs(argc, argv);
+ return hiddenapi.ProcessDexFiles() ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc
new file mode 100644
index 0000000000..1f37f40f6e
--- /dev/null
+++ b/tools/hiddenapi/hiddenapi_test.cc
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <fstream>
+
+#include "base/unix_file/fd_file.h"
+#include "common_runtime_test.h"
+#include "dex/art_dex_file_loader.h"
+#include "dex/dex_file-inl.h"
+#include "exec_utils.h"
+#include "zip_archive.h"
+
+namespace art {
+
+class HiddenApiTest : public CommonRuntimeTest {
+ protected:
+ std::string GetHiddenApiCmd() {
+ std::string file_path = GetTestAndroidRoot();
+ file_path += "/bin/hiddenapi";
+ if (kIsDebugBuild) {
+ file_path += "d";
+ }
+ if (!OS::FileExists(file_path.c_str())) {
+ LOG(FATAL) << "Could not find binary " << file_path;
+ UNREACHABLE();
+ }
+ return file_path;
+ }
+
+ std::unique_ptr<const DexFile> RunHiddenApi(const ScratchFile& light_greylist,
+ const ScratchFile& dark_greylist,
+ const ScratchFile& blacklist,
+ const std::vector<std::string>& extra_args,
+ ScratchFile* out_dex) {
+ std::string error;
+ ZipArchive* jar = ZipArchive::Open(GetTestDexFileName("HiddenApi").c_str(), &error);
+ if (jar == nullptr) {
+ LOG(FATAL) << "Could not open test file " << GetTestDexFileName("HiddenApi") << ": " << error;
+ UNREACHABLE();
+ }
+ ZipEntry* jar_classes_dex = jar->Find("classes.dex", &error);
+ if (jar_classes_dex == nullptr) {
+ LOG(FATAL) << "Could not find classes.dex in test file " << GetTestDexFileName("HiddenApi")
+ << ": " << error;
+ UNREACHABLE();
+ } else if (!jar_classes_dex->ExtractToFile(*out_dex->GetFile(), &error)) {
+ LOG(FATAL) << "Could not extract classes.dex from test file "
+ << GetTestDexFileName("HiddenApi") << ": " << error;
+ UNREACHABLE();
+ }
+
+ std::vector<std::string> argv_str;
+ argv_str.push_back(GetHiddenApiCmd());
+ argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end());
+ argv_str.push_back("--dex=" + out_dex->GetFilename());
+ argv_str.push_back("--light-greylist=" + light_greylist.GetFilename());
+ argv_str.push_back("--dark-greylist=" + dark_greylist.GetFilename());
+ argv_str.push_back("--blacklist=" + blacklist.GetFilename());
+ int return_code = ExecAndReturnCode(argv_str, &error);
+ if (return_code != 0) {
+ LOG(FATAL) << "HiddenApi binary exited with unexpected return code " << return_code;
+ }
+ return OpenDex(*out_dex);
+ }
+
+ std::unique_ptr<const DexFile> OpenDex(const ScratchFile& file) {
+ ArtDexFileLoader dex_loader;
+ std::string error_msg;
+
+ File fd(file.GetFilename(), O_RDONLY, /* check_usage */ false);
+ if (fd.Fd() == -1) {
+ LOG(FATAL) << "Unable to open file '" << file.GetFilename() << "': " << strerror(errno);
+ UNREACHABLE();
+ }
+
+ std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(
+ fd.Release(), /* location */ file.GetFilename(), /* verify */ false,
+ /* verify_checksum */ true, /* mmap_shared */ false, &error_msg));
+ if (dex_file.get() == nullptr) {
+ LOG(FATAL) << "Open failed for '" << file.GetFilename() << "' " << error_msg;
+ UNREACHABLE();
+ } else if (!dex_file->IsStandardDexFile()) {
+ LOG(FATAL) << "Expected a standard dex file '" << file.GetFilename() << "'";
+ UNREACHABLE();
+ }
+
+ return dex_file;
+ }
+
+ std::ofstream OpenStream(const ScratchFile& file) {
+ std::ofstream ofs(file.GetFilename(), std::ofstream::out);
+ if (ofs.fail()) {
+ LOG(FATAL) << "Open failed for '" << file.GetFilename() << "' " << strerror(errno);
+ UNREACHABLE();
+ }
+ return ofs;
+ }
+
+ const DexFile::ClassDef& FindClass(const char* desc, const DexFile& dex_file) {
+ for (uint32_t i = 0; i < dex_file.NumClassDefs(); ++i) {
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
+ if (strcmp(desc, dex_file.GetClassDescriptor(class_def)) == 0) {
+ return class_def;
+ }
+ }
+ LOG(FATAL) << "Could not find class " << desc;
+ UNREACHABLE();
+ }
+
+ DexHiddenAccessFlags::ApiList GetFieldHiddenFlags(const char* name,
+ uint32_t expected_visibility,
+ const DexFile::ClassDef& class_def,
+ const DexFile& dex_file) {
+ const uint8_t* class_data = dex_file.GetClassData(class_def);
+ if (class_data == nullptr) {
+ LOG(FATAL) << "Class " << dex_file.GetClassDescriptor(class_def) << " has no data";
+ UNREACHABLE();
+ }
+
+ for (ClassDataItemIterator it(dex_file, class_data); it.HasNext(); it.Next()) {
+ if (it.IsAtMethod()) {
+ break;
+ }
+ const DexFile::FieldId& fid = dex_file.GetFieldId(it.GetMemberIndex());
+ if (strcmp(name, dex_file.GetFieldName(fid)) == 0) {
+ uint32_t actual_visibility = it.GetFieldAccessFlags() & kAccVisibilityFlags;
+ if (actual_visibility != expected_visibility) {
+ LOG(FATAL) << "Field " << name << " in class " << dex_file.GetClassDescriptor(class_def)
+ << " does not have the expected visibility flags (" << expected_visibility
+ << " != " << actual_visibility << ")";
+ UNREACHABLE();
+ }
+ return it.DecodeHiddenAccessFlags();
+ }
+ }
+
+ LOG(FATAL) << "Could not find field " << name << " in class "
+ << dex_file.GetClassDescriptor(class_def);
+ UNREACHABLE();
+ }
+
+ DexHiddenAccessFlags::ApiList GetMethodHiddenFlags(const char* name,
+ uint32_t expected_visibility,
+ bool expected_native,
+ const DexFile::ClassDef& class_def,
+ const DexFile& dex_file) {
+ const uint8_t* class_data = dex_file.GetClassData(class_def);
+ if (class_data == nullptr) {
+ LOG(FATAL) << "Class " << dex_file.GetClassDescriptor(class_def) << " has no data";
+ UNREACHABLE();
+ }
+
+ for (ClassDataItemIterator it(dex_file, class_data); it.HasNext(); it.Next()) {
+ if (!it.IsAtMethod()) {
+ continue;
+ }
+ const DexFile::MethodId& mid = dex_file.GetMethodId(it.GetMemberIndex());
+ if (strcmp(name, dex_file.GetMethodName(mid)) == 0) {
+ if (expected_native != it.MemberIsNative()) {
+ LOG(FATAL) << "Expected native=" << expected_native << " for method " << name
+ << " in class " << dex_file.GetClassDescriptor(class_def);
+ UNREACHABLE();
+ }
+ uint32_t actual_visibility = it.GetMethodAccessFlags() & kAccVisibilityFlags;
+ if (actual_visibility != expected_visibility) {
+ LOG(FATAL) << "Method " << name << " in class " << dex_file.GetClassDescriptor(class_def)
+ << " does not have the expected visibility flags (" << expected_visibility
+ << " != " << actual_visibility << ")";
+ UNREACHABLE();
+ }
+ return it.DecodeHiddenAccessFlags();
+ }
+ }
+
+ LOG(FATAL) << "Could not find method " << name << " in class "
+ << dex_file.GetClassDescriptor(class_def);
+ UNREACHABLE();
+ }
+
+ DexHiddenAccessFlags::ApiList GetIFieldHiddenFlags(const DexFile& dex_file) {
+ return GetFieldHiddenFlags("ifield", kAccPublic, FindClass("LMain;", dex_file), dex_file);
+ }
+
+ DexHiddenAccessFlags::ApiList GetSFieldHiddenFlags(const DexFile& dex_file) {
+ return GetFieldHiddenFlags("sfield", kAccPrivate, FindClass("LMain;", dex_file), dex_file);
+ }
+
+ DexHiddenAccessFlags::ApiList GetIMethodHiddenFlags(const DexFile& dex_file) {
+ return GetMethodHiddenFlags(
+ "imethod", 0, /* native */ false, FindClass("LMain;", dex_file), dex_file);
+ }
+
+ DexHiddenAccessFlags::ApiList GetSMethodHiddenFlags(const DexFile& dex_file) {
+ return GetMethodHiddenFlags(
+ "smethod", kAccPublic, /* native */ false, FindClass("LMain;", dex_file), dex_file);
+ }
+
+ DexHiddenAccessFlags::ApiList GetINMethodHiddenFlags(const DexFile& dex_file) {
+ return GetMethodHiddenFlags(
+ "inmethod", kAccPublic, /* native */ true, FindClass("LMain;", dex_file), dex_file);
+ }
+
+ DexHiddenAccessFlags::ApiList GetSNMethodHiddenFlags(const DexFile& dex_file) {
+ return GetMethodHiddenFlags(
+ "snmethod", kAccProtected, /* native */ true, FindClass("LMain;", dex_file), dex_file);
+ }
+};
+
+TEST_F(HiddenApiTest, InstanceFieldNoMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->ifield:LBadType1;" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->ifield:LBadType2;" << std::endl;
+ OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kWhitelist, GetIFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceFieldLightGreylistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->ifield:I" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->ifield:LBadType2;" << std::endl;
+ OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kLightGreylist, GetIFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceFieldDarkGreylistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->ifield:LBadType1;" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->ifield:I" << std::endl;
+ OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetIFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceFieldBlacklistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->ifield:LBadType1;" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->ifield:LBadType2;" << std::endl;
+ OpenStream(blacklist) << "LMain;->ifield:I" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetIFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch1) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->ifield:LBadType1;" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->ifield:I" << std::endl;
+ OpenStream(blacklist) << "LMain;->ifield:I" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetIFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch2) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->ifield:I" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->ifield:LBadType2;" << std::endl;
+ OpenStream(blacklist) << "LMain;->ifield:I" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetIFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch3) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->ifield:I" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->ifield:I" << std::endl;
+ OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetIFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticFieldNoMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->sfield:LBadType1;" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->sfield:LBadType2;" << std::endl;
+ OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kWhitelist, GetSFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticFieldLightGreylistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->sfield:LBadType2;" << std::endl;
+ OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kLightGreylist, GetSFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticFieldDarkGreylistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->sfield:LBadType1;" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
+ OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetSFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticFieldBlacklistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->sfield:LBadType1;" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->sfield:LBadType2;" << std::endl;
+ OpenStream(blacklist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticFieldTwoListsMatch1) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->sfield:LBadType1;" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
+ OpenStream(blacklist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticFieldTwoListsMatch2) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->sfield:LBadType2;" << std::endl;
+ OpenStream(blacklist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticFieldTwoListsMatch3) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
+ OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetSFieldHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceMethodNoMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->imethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->imethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kWhitelist, GetIMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceMethodLightGreylistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->imethod(J)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->imethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kLightGreylist, GetIMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceMethodDarkGreylistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->imethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->imethod(J)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetIMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceMethodBlacklistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->imethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->imethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->imethod(J)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetIMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch1) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->imethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->imethod(J)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->imethod(J)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetIMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch2) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->imethod(J)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->imethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->imethod(J)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetIMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch3) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->imethod(J)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->imethod(J)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetIMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticMethodNoMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->smethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->smethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kWhitelist, GetSMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticMethodLightGreylistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->smethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kLightGreylist, GetSMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticMethodDarkGreylistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->smethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetSMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticMethodBlacklistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->smethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->smethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticMethodTwoListsMatch1) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->smethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticMethodTwoListsMatch2) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->smethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticMethodTwoListsMatch3) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetSMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceNativeMethodNoMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->inmethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->inmethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kWhitelist, GetINMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceNativeMethodLightGreylistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->inmethod(C)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->inmethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kLightGreylist, GetINMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceNativeMethodDarkGreylistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->inmethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->inmethod(C)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetINMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceNativeMethodBlacklistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->inmethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->inmethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->inmethod(C)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetINMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch1) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->inmethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->inmethod(C)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->inmethod(C)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetINMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch2) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->inmethod(C)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->inmethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->inmethod(C)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetINMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch3) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->inmethod(C)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->inmethod(C)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetINMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticNativeMethodNoMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->snmethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->snmethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kWhitelist, GetSNMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticNativeMethodLightGreylistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->snmethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kLightGreylist, GetSNMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticNativeMethodDarkGreylistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->snmethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetSNMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticNativeMethodBlacklistMatch) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->snmethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->snmethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSNMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch1) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->snmethod(LBadType1;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSNMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch2) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->snmethod(LBadType2;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSNMethodHiddenFlags(*dex_file));
+}
+
+TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch3) {
+ ScratchFile dex, light_greylist, dark_greylist, blacklist;
+ OpenStream(light_greylist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
+ OpenStream(dark_greylist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
+ OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl;
+ auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
+ ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetSNMethodHiddenFlags(*dex_file));
+}
+
+} // namespace art