Merge "ART: Templatize DexFileVerifier::CheckIntraIdSection()."
diff --git a/Android.bp b/Android.bp
index 59f7abf..34a6469 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,6 +15,7 @@
"libbase",
"liblz4",
"liblzma",
+ "libmetricslogger_static",
]
subdirs = [
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 5a25a6c..48da755 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -643,6 +643,16 @@
return Result::SuccessNoValue();
}
+ if (option == "profile-aot-code") {
+ existing.profile_aot_code_ = true;
+ return Result::SuccessNoValue();
+ }
+
+ if (option == "save-without-jit-notifications") {
+ existing.wait_for_jit_notifications_to_save_ = false;
+ return Result::SuccessNoValue();
+ }
+
// The rest of these options are always the wildcard from '-Xps-*'
std::string suffix = RemovePrefix(option);
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 2d82d79..933be4f 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -51,6 +51,7 @@
implicit_suspend_checks_(false),
compile_pic_(false),
dump_timings_(false),
+ dump_pass_timings_(false),
dump_stats_(false),
verbose_methods_(),
abort_on_hard_verifier_failure_(false),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index cdd9d4d..cee989b 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -270,6 +270,10 @@
return dump_timings_;
}
+ bool GetDumpPassTimings() const {
+ return dump_pass_timings_;
+ }
+
bool GetDumpStats() const {
return dump_stats_;
}
@@ -316,6 +320,7 @@
bool implicit_suspend_checks_;
bool compile_pic_;
bool dump_timings_;
+ bool dump_pass_timings_;
bool dump_stats_;
// Vector of methods to have verbose output enabled for.
diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h
index 3b18db0..32fc887 100644
--- a/compiler/driver/compiler_options_map-inl.h
+++ b/compiler/driver/compiler_options_map-inl.h
@@ -85,6 +85,10 @@
options->dump_timings_ = true;
}
+ if (map.Exists(Base::DumpPassTimings)) {
+ options->dump_pass_timings_ = true;
+ }
+
if (map.Exists(Base::DumpStats)) {
options->dump_stats_ = true;
}
@@ -146,6 +150,9 @@
.Define({"--dump-timings"})
.IntoKey(Map::DumpTimings)
+ .Define({"--dump-pass-timings"})
+ .IntoKey(Map::DumpPassTimings)
+
.Define({"--dump-stats"})
.IntoKey(Map::DumpStats)
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
index acddae7..529d43f 100644
--- a/compiler/driver/compiler_options_map.def
+++ b/compiler/driver/compiler_options_map.def
@@ -60,6 +60,7 @@
COMPILER_OPTIONS_KEY (bool, DeduplicateCode, true)
COMPILER_OPTIONS_KEY (Unit, CountHotnessInCompiledCode)
COMPILER_OPTIONS_KEY (Unit, DumpTimings)
+COMPILER_OPTIONS_KEY (Unit, DumpPassTimings)
COMPILER_OPTIONS_KEY (Unit, DumpStats)
#undef COMPILER_OPTIONS_KEY
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 6e2c994..c4977de 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -109,7 +109,7 @@
: graph_(graph),
last_seen_graph_size_(0),
cached_method_name_(),
- timing_logger_enabled_(compiler_driver->GetCompilerOptions().GetDumpTimings()),
+ timing_logger_enabled_(compiler_driver->GetCompilerOptions().GetDumpPassTimings()),
timing_logger_(timing_logger_enabled_ ? GetMethodName() : "", true, true),
disasm_info_(graph->GetAllocator()),
visualizer_oss_(),
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 4fafca9..18548ba 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -294,6 +294,8 @@
use_clang_lld: true,
},
},
+ // b/79417743, oatdump 32-bit tests failed with clang lld
+ use_clang_lld: false,
ldflags: [
// We need this because GC stress mode makes use of
// _Unwind_GetIP and _Unwind_Backtrace and the symbols are also
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index df38ee3..3252354 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -350,6 +350,9 @@
UsageError("");
UsageError(" --dump-timings: display a breakdown of where time was spent");
UsageError("");
+ UsageError(" --dump-pass-timings: display a breakdown of time spent in optimization");
+ UsageError(" passes for each compiled method.");
+ UsageError("");
UsageError(" -g");
UsageError(" --generate-debug-info: Generate debug information for native debugging,");
UsageError(" such as stack unwinding information, ELF symbols and DWARF sections.");
diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h
index b62d044..1aaeabd 100644
--- a/libdexfile/dex/hidden_api_access_flags.h
+++ b/libdexfile/dex/hidden_api_access_flags.h
@@ -86,12 +86,10 @@
}
static ALWAYS_INLINE ApiList DecodeFromRuntime(uint32_t runtime_access_flags) {
- if ((runtime_access_flags & kAccIntrinsic) != 0) {
- return kWhitelist;
- } else {
- uint32_t int_value = (runtime_access_flags & kAccHiddenApiBits) >> kAccFlagsShift;
- return static_cast<ApiList>(int_value);
- }
+ // This is used in the fast path, only DCHECK here.
+ DCHECK_EQ(runtime_access_flags & kAccIntrinsic, 0u);
+ uint32_t int_value = (runtime_access_flags & kAccHiddenApiBits) >> kAccFlagsShift;
+ return static_cast<ApiList>(int_value);
}
static ALWAYS_INLINE uint32_t EncodeForRuntime(uint32_t runtime_access_flags, ApiList value) {
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index be12c8e..535acdf 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -24,6 +24,8 @@
shared_libs: ["libcutils"],
},
},
+ // b/79417743, oatdump 32-bit tests failed with clang lld
+ use_clang_lld: false,
header_libs: [
"art_cmdlineparser_headers",
],
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 28bee3d..1ef5bf0 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -397,6 +397,7 @@
"libbacktrace",
"liblz4",
"liblog",
+ "libmetricslogger",
// For atrace, properties, ashmem, set_sched_policy and socket_peer_is_trusted.
"libcutils",
// For common macros.
diff --git a/runtime/art_field.h b/runtime/art_field.h
index 29d71af..f39af39 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -20,6 +20,7 @@
#include <jni.h>
#include "dex/dex_file_types.h"
+#include "dex/hidden_api_access_flags.h"
#include "dex/modifiers.h"
#include "dex/primitive.h"
#include "gc_root.h"
@@ -179,6 +180,10 @@
return (GetAccessFlags() & kAccVolatile) != 0;
}
+ HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return HiddenApiAccessFlags::DecodeFromRuntime(GetAccessFlags());
+ }
+
// Returns an instance field with this offset in the given class or null if not found.
// If kExactOffset is true then we only find the matching offset, not the field containing the
// offset.
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 1565644..c1fac36 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -384,19 +384,68 @@
return (GetAccessFlags<kReadBarrierOption>() & kAccSingleImplementation) != 0;
}
-inline bool ArtMethod::IsHiddenIntrinsic(uint32_t ordinal) {
- switch (static_cast<Intrinsics>(ordinal)) {
- case Intrinsics::kReferenceGetReferent:
- case Intrinsics::kSystemArrayCopyChar:
- case Intrinsics::kStringGetCharsNoCheck:
- case Intrinsics::kVarHandleFullFence:
- case Intrinsics::kVarHandleAcquireFence:
- case Intrinsics::kVarHandleReleaseFence:
- case Intrinsics::kVarHandleLoadLoadFence:
- case Intrinsics::kVarHandleStoreStoreFence:
- return true;
- default:
- return false;
+inline HiddenApiAccessFlags::ApiList ArtMethod::GetHiddenApiAccessFlags()
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (UNLIKELY(IsIntrinsic())) {
+ switch (static_cast<Intrinsics>(GetIntrinsic())) {
+ case Intrinsics::kSystemArrayCopyChar:
+ case Intrinsics::kStringGetCharsNoCheck:
+ case Intrinsics::kReferenceGetReferent:
+ // These intrinsics are on the light greylist and will fail a DCHECK in
+ // SetIntrinsic() if their flags change on the respective dex methods.
+ // Note that the DCHECK currently won't fail if the dex methods are
+ // whitelisted, e.g. in the core image (b/77733081). As a result, we
+ // might print warnings but we won't change the semantics.
+ return HiddenApiAccessFlags::kLightGreylist;
+ case Intrinsics::kVarHandleFullFence:
+ case Intrinsics::kVarHandleAcquireFence:
+ case Intrinsics::kVarHandleReleaseFence:
+ case Intrinsics::kVarHandleLoadLoadFence:
+ case Intrinsics::kVarHandleStoreStoreFence:
+ case Intrinsics::kVarHandleCompareAndExchange:
+ case Intrinsics::kVarHandleCompareAndExchangeAcquire:
+ case Intrinsics::kVarHandleCompareAndExchangeRelease:
+ case Intrinsics::kVarHandleCompareAndSet:
+ case Intrinsics::kVarHandleGet:
+ case Intrinsics::kVarHandleGetAcquire:
+ case Intrinsics::kVarHandleGetAndAdd:
+ case Intrinsics::kVarHandleGetAndAddAcquire:
+ case Intrinsics::kVarHandleGetAndAddRelease:
+ case Intrinsics::kVarHandleGetAndBitwiseAnd:
+ case Intrinsics::kVarHandleGetAndBitwiseAndAcquire:
+ case Intrinsics::kVarHandleGetAndBitwiseAndRelease:
+ case Intrinsics::kVarHandleGetAndBitwiseOr:
+ case Intrinsics::kVarHandleGetAndBitwiseOrAcquire:
+ case Intrinsics::kVarHandleGetAndBitwiseOrRelease:
+ case Intrinsics::kVarHandleGetAndBitwiseXor:
+ case Intrinsics::kVarHandleGetAndBitwiseXorAcquire:
+ case Intrinsics::kVarHandleGetAndBitwiseXorRelease:
+ case Intrinsics::kVarHandleGetAndSet:
+ case Intrinsics::kVarHandleGetAndSetAcquire:
+ case Intrinsics::kVarHandleGetAndSetRelease:
+ case Intrinsics::kVarHandleGetOpaque:
+ case Intrinsics::kVarHandleGetVolatile:
+ case Intrinsics::kVarHandleSet:
+ case Intrinsics::kVarHandleSetOpaque:
+ case Intrinsics::kVarHandleSetRelease:
+ case Intrinsics::kVarHandleSetVolatile:
+ case Intrinsics::kVarHandleWeakCompareAndSet:
+ case Intrinsics::kVarHandleWeakCompareAndSetAcquire:
+ case Intrinsics::kVarHandleWeakCompareAndSetPlain:
+ case Intrinsics::kVarHandleWeakCompareAndSetRelease:
+ // These intrinsics are on the blacklist and will fail a DCHECK in
+ // SetIntrinsic() if their flags change on the respective dex methods.
+ // Note that the DCHECK currently won't fail if the dex methods are
+ // whitelisted, e.g. in the core image (b/77733081). Given that they are
+ // exclusively VarHandle intrinsics, they should not be used outside
+ // tests that do not enable hidden API checks.
+ return HiddenApiAccessFlags::kBlacklist;
+ default:
+ // Remaining intrinsics are public API. We DCHECK that in SetIntrinsic().
+ return HiddenApiAccessFlags::kWhitelist;
+ }
+ } else {
+ return HiddenApiAccessFlags::DecodeFromRuntime(GetAccessFlags());
}
}
@@ -422,7 +471,7 @@
bool is_default_conflict = IsDefaultConflicting();
bool is_compilable = IsCompilable();
bool must_count_locks = MustCountLocks();
- HiddenApiAccessFlags::ApiList hidden_api_list = GetHiddenApiAccessFlags();
+ HiddenApiAccessFlags::ApiList hidden_api_flags = GetHiddenApiAccessFlags();
SetAccessFlags(new_value);
DCHECK_EQ(java_flags, (GetAccessFlags() & kAccJavaFlagsMask));
DCHECK_EQ(is_constructor, IsConstructor());
@@ -436,14 +485,14 @@
DCHECK_EQ(is_default_conflict, IsDefaultConflicting());
DCHECK_EQ(is_compilable, IsCompilable());
DCHECK_EQ(must_count_locks, MustCountLocks());
- if (kIsDebugBuild) {
- if (IsHiddenIntrinsic(intrinsic)) {
- // Special case some of our intrinsics because the access flags clash
- // with the intrinsics ordinal.
- DCHECK_EQ(HiddenApiAccessFlags::kWhitelist, GetHiddenApiAccessFlags());
- } else {
- DCHECK_EQ(hidden_api_list, GetHiddenApiAccessFlags());
- }
+ // Only DCHECK that we have preserved the hidden API access flags if the
+ // original method was not on the whitelist. This is because the core image
+ // does not have the access flags set (b/77733081). It is fine to hard-code
+ // these because (a) warnings on greylist do not change semantics, and
+ // (b) only VarHandle intrinsics are blacklisted at the moment and they
+ // should not be used outside tests with disabled API checks.
+ if (hidden_api_flags != HiddenApiAccessFlags::kWhitelist) {
+ DCHECK_EQ(hidden_api_flags, GetHiddenApiAccessFlags());
}
} else {
SetAccessFlags(new_value);
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 64d2932..acaa4a6 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -341,9 +341,7 @@
AddAccessFlags(kAccMustCountLocks);
}
- HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags() {
- return HiddenApiAccessFlags::DecodeFromRuntime(GetAccessFlags());
- }
+ HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_);
// Returns true if this method could be overridden by a default method.
bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -873,9 +871,6 @@
} while (!access_flags_.compare_exchange_weak(old_access_flags, new_access_flags));
}
- // Returns true if the given intrinsic is considered hidden.
- bool IsHiddenIntrinsic(uint32_t ordinal);
-
DISALLOW_COPY_AND_ASSIGN(ArtMethod); // Need to use CopyFrom to deal with 32 vs 64 bits.
};
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 57c0d9d..44445ae 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -7884,6 +7884,40 @@
return resolved;
}
+// Returns true if `method` is either null or hidden.
+// Does not print any warnings if it is hidden.
+static bool CheckNoSuchMethod(ArtMethod* method,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return method == nullptr ||
+ hiddenapi::GetMemberAction(method,
+ class_loader,
+ dex_cache,
+ hiddenapi::kNone) // do not print warnings
+ == hiddenapi::kDeny;
+}
+
+ArtMethod* ClassLinker::FindIncompatibleMethod(ObjPtr<mirror::Class> klass,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader,
+ uint32_t method_idx) {
+ if (klass->IsInterface()) {
+ ArtMethod* method = klass->FindClassMethod(dex_cache, method_idx, image_pointer_size_);
+ return CheckNoSuchMethod(method, dex_cache, class_loader) ? nullptr : method;
+ } else {
+ // If there was an interface method with the same signature, we would have
+ // found it in the "copied" methods. Only DCHECK that the interface method
+ // really does not exist.
+ if (kIsDebugBuild) {
+ ArtMethod* method =
+ klass->FindInterfaceMethod(dex_cache, method_idx, image_pointer_size_);
+ DCHECK(CheckNoSuchMethod(method, dex_cache, class_loader));
+ }
+ return nullptr;
+ }
+}
+
template <ClassLinker::ResolveMode kResolveMode>
ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx,
Handle<mirror::DexCache> dex_cache,
@@ -7959,13 +7993,7 @@
// If we had a method, or if we can find one with another lookup type,
// it's an incompatible-class-change error.
if (resolved == nullptr) {
- if (klass->IsInterface()) {
- resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size);
- } else {
- // If there was an interface method with the same signature,
- // we would have found it also in the "copied" methods.
- DCHECK(klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size) == nullptr);
- }
+ resolved = FindIncompatibleMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx);
}
if (resolved != nullptr) {
ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index fa70f65..e935d1d 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -329,6 +329,15 @@
uint32_t method_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Find a method using the wrong lookup mechanism. If `klass` is an interface,
+ // search for a class method. If it is a class, search for an interface method.
+ // This is useful when throwing IncompatibleClassChangeError.
+ ArtMethod* FindIncompatibleMethod(ObjPtr<mirror::Class> klass,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader,
+ uint32_t method_idx)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Resolve a method with a given ID from the DexFile associated with the given DexCache
// and ClassLoader, storing the result in DexCache. The ClassLinker and ClassLoader are
// used as in ResolveType. What is unique is the method type argument which is used to
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index 0e72f27..9445ae0 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <metricslogger/metrics_logger.h>
+
#include "hidden_api.h"
#include <nativehelper/scoped_local_ref.h>
@@ -22,11 +24,28 @@
#include "thread-current-inl.h"
#include "well_known_classes.h"
+using android::metricslogger::ComplexEventLogger;
+using android::metricslogger::ACTION_HIDDEN_API_ACCESSED;
+using android::metricslogger::FIELD_HIDDEN_API_ACCESS_METHOD;
+using android::metricslogger::FIELD_HIDDEN_API_ACCESS_DENIED;
+using android::metricslogger::FIELD_HIDDEN_API_SIGNATURE;
+
namespace art {
namespace hiddenapi {
+// Set to true if we should always print a warning in logcat for all hidden API accesses, not just
+// dark grey and black. This can be set to true for developer preview / beta builds, but should be
+// false for public release builds.
+// Note that when flipping this flag, you must also update the expectations of test 674-hiddenapi
+// as it affects whether or not we warn for light grey APIs that have been added to the exemptions
+// list.
+static constexpr bool kLogAllAccesses = true;
+
static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
switch (value) {
+ case kNone:
+ LOG(FATAL) << "Internal access to hidden API should not be logged";
+ UNREACHABLE();
case kReflection:
os << "reflection";
break;
@@ -46,42 +65,47 @@
// GetMemberAction-related static_asserts.
static_assert(
- EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) &&
EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) &&
EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist),
"Mismatch between EnforcementPolicy and ApiList enums");
static_assert(
- EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList &&
+ EnforcementPolicy::kJustWarn < EnforcementPolicy::kDarkGreyAndBlackList &&
EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
"EnforcementPolicy values ordering not correct");
namespace detail {
MemberSignature::MemberSignature(ArtField* field) {
- member_type_ = "field";
- signature_parts_ = {
- field->GetDeclaringClass()->GetDescriptor(&tmp_),
- "->",
- field->GetName(),
- ":",
- field->GetTypeDescriptor()
- };
+ class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_);
+ member_name_ = field->GetName();
+ type_signature_ = field->GetTypeDescriptor();
+ type_ = kField;
}
MemberSignature::MemberSignature(ArtMethod* method) {
- member_type_ = "method";
- signature_parts_ = {
- method->GetDeclaringClass()->GetDescriptor(&tmp_),
- "->",
- method->GetName(),
- method->GetSignature().ToString()
- };
+ // If this is a proxy method, print the signature of the interface method.
+ method = method->GetInterfaceMethodIfProxy(
+ Runtime::Current()->GetClassLinker()->GetImagePointerSize());
+
+ class_name_ = method->GetDeclaringClass()->GetDescriptor(&tmp_);
+ member_name_ = method->GetName();
+ type_signature_ = method->GetSignature().ToString();
+ type_ = kMethod;
+}
+
+inline std::vector<const char*> MemberSignature::GetSignatureParts() const {
+ if (type_ == kField) {
+ return { class_name_.c_str(), "->", member_name_.c_str(), ":", type_signature_.c_str() };
+ } else {
+ DCHECK_EQ(type_, kMethod);
+ return { class_name_.c_str(), "->", member_name_.c_str(), type_signature_.c_str() };
+ }
}
bool MemberSignature::DoesPrefixMatch(const std::string& prefix) const {
size_t pos = 0;
- for (const std::string& part : signature_parts_) {
- size_t count = std::min(prefix.length() - pos, part.length());
+ for (const char* part : GetSignatureParts()) {
+ size_t count = std::min(prefix.length() - pos, strlen(part));
if (prefix.compare(pos, count, part, 0, count) == 0) {
pos += count;
} else {
@@ -103,42 +127,115 @@
}
void MemberSignature::Dump(std::ostream& os) const {
- for (std::string part : signature_parts_) {
+ for (const char* part : GetSignatureParts()) {
os << part;
}
}
void MemberSignature::WarnAboutAccess(AccessMethod access_method,
HiddenApiAccessFlags::ApiList list) {
- LOG(WARNING) << "Accessing hidden " << member_type_ << " " << Dumpable<MemberSignature>(*this)
- << " (" << list << ", " << access_method << ")";
+ LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ")
+ << Dumpable<MemberSignature>(*this) << " (" << list << ", " << access_method << ")";
+}
+// Convert an AccessMethod enum to a value for logging from the proto enum.
+// This method may look odd (the enum values are current the same), but it
+// prevents coupling the internal enum to the proto enum (which should never
+// be changed) so that we are free to change the internal one if necessary in
+// future.
+inline static int32_t GetEnumValueForLog(AccessMethod access_method) {
+ switch (access_method) {
+ case kNone:
+ return android::metricslogger::ACCESS_METHOD_NONE;
+ case kReflection:
+ return android::metricslogger::ACCESS_METHOD_REFLECTION;
+ case kJNI:
+ return android::metricslogger::ACCESS_METHOD_JNI;
+ case kLinking:
+ return android::metricslogger::ACCESS_METHOD_LINKING;
+ default:
+ DCHECK(false);
+ }
+}
+
+void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) {
+ if (access_method == kLinking) {
+ // Linking warnings come from static analysis/compilation of the bytecode
+ // and can contain false positives (i.e. code that is never run). We choose
+ // not to log these in the event log.
+ return;
+ }
+ ComplexEventLogger log_maker(ACTION_HIDDEN_API_ACCESSED);
+ log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_METHOD, GetEnumValueForLog(access_method));
+ if (action_taken == kDeny) {
+ log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_DENIED, 1);
+ }
+ std::ostringstream signature_str;
+ Dump(signature_str);
+ log_maker.AddTaggedData(FIELD_HIDDEN_API_SIGNATURE, signature_str.str());
+ log_maker.Record();
+}
+
+static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) {
+ return true;
+}
+
+static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtMethod* method) {
+ return !method->IsIntrinsic();
}
template<typename T>
-Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) {
+static ALWAYS_INLINE void MaybeWhitelistMember(Runtime* runtime, T* member)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (CanUpdateMemberAccessFlags(member) && runtime->ShouldDedupeHiddenApiWarnings()) {
+ member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
+ member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
+ }
+}
+
+template<typename T>
+Action GetMemberActionImpl(T* member,
+ HiddenApiAccessFlags::ApiList api_list,
+ Action action,
+ AccessMethod access_method) {
+ DCHECK_NE(action, kAllow);
+
// Get the signature, we need it later.
MemberSignature member_signature(member);
Runtime* runtime = Runtime::Current();
- if (action == kDeny) {
- // If we were about to deny, check for an exemption first.
- // Exempted APIs are treated as light grey list.
+ // Check for an exemption first. Exempted APIs are treated as white list.
+ // We only do this if we're about to deny, or if the app is debuggable. This is because:
+ // - we only print a warning for light greylist violations for debuggable apps
+ // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs.
+ // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever
+ // possible.
+ if (kLogAllAccesses || action == kDeny || runtime->IsJavaDebuggable()) {
if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
- action = kAllowButWarn;
+ action = kAllow;
// Avoid re-examining the exemption list next time.
- // Note this results in the warning below showing "light greylist", which
- // seems like what one would expect. Exemptions effectively add new members to
- // the light greylist.
- member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
- member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist));
+ // Note this results in no warning for the member, which seems like what one would expect.
+ // Exemptions effectively adds new members to the whitelist.
+ MaybeWhitelistMember(runtime, member);
+ return kAllow;
+ }
+
+ if (access_method != kNone) {
+ // Print a log message with information about this class member access.
+ // We do this if we're about to block access, or the app is debuggable.
+ member_signature.WarnAboutAccess(access_method, api_list);
}
}
- // Print a log message with information about this class member access.
- // We do this regardless of whether we block the access or not.
- member_signature.WarnAboutAccess(access_method,
- HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags()));
+ if (kIsTargetBuild) {
+ uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
+ // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
+ static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
+ if (eventLogSampleRate != 0 &&
+ (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) {
+ member_signature.LogAccessToEventLog(access_method, action);
+ }
+ }
if (action == kDeny) {
// Block access
@@ -148,16 +245,15 @@
// Allow access to this member but print a warning.
DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
- // Depending on a runtime flag, we might move the member into whitelist and
- // skip the warning the next time the member is accessed.
- if (runtime->ShouldDedupeHiddenApiWarnings()) {
- member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
- member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
- }
+ if (access_method != kNone) {
+ // Depending on a runtime flag, we might move the member into whitelist and
+ // skip the warning the next time the member is accessed.
+ MaybeWhitelistMember(runtime, member);
- // If this action requires a UI warning, set the appropriate flag.
- if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
- runtime->SetPendingHiddenApiWarning(true);
+ // If this action requires a UI warning, set the appropriate flag.
+ if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
+ runtime->SetPendingHiddenApiWarning(true);
+ }
}
return action;
@@ -165,9 +261,11 @@
// Need to instantiate this.
template Action GetMemberActionImpl<ArtField>(ArtField* member,
+ HiddenApiAccessFlags::ApiList api_list,
Action action,
AccessMethod access_method);
template Action GetMemberActionImpl<ArtMethod>(ArtMethod* member,
+ HiddenApiAccessFlags::ApiList api_list,
Action action,
AccessMethod access_method);
} // namespace detail
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index ffdeacb..65c6406 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -33,7 +33,7 @@
// frameworks/base/core/java/android/content/pm/ApplicationInfo.java
enum class EnforcementPolicy {
kNoChecks = 0,
- kAllLists = 1, // ban anything but whitelist
+ kJustWarn = 1, // keep checks enabled, but allow everything (enables logging)
kDarkGreyAndBlackList = 2, // ban dark grey & blacklist
kBlacklistOnly = 3, // ban blacklist violations only
kMax = kBlacklistOnly,
@@ -53,22 +53,37 @@
};
enum AccessMethod {
+ kNone, // internal test that does not correspond to an actual access by app
kReflection,
kJNI,
kLinking,
};
-inline Action GetActionFromAccessFlags(uint32_t access_flags) {
+// Do not change the values of items in this enum, as they are written to the
+// event log for offline analysis. Any changes will interfere with that analysis.
+enum AccessContextFlags {
+ // Accessed member is a field if this bit is set, else a method
+ kMemberIsField = 1 << 0,
+ // Indicates if access was denied to the member, instead of just printing a warning.
+ kAccessDenied = 1 << 1,
+};
+
+inline Action GetActionFromAccessFlags(HiddenApiAccessFlags::ApiList api_list) {
+ if (api_list == HiddenApiAccessFlags::kWhitelist) {
+ return kAllow;
+ }
+
EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
if (policy == EnforcementPolicy::kNoChecks) {
// Exit early. Nothing to enforce.
return kAllow;
}
- HiddenApiAccessFlags::ApiList api_list = HiddenApiAccessFlags::DecodeFromRuntime(access_flags);
- if (api_list == HiddenApiAccessFlags::kWhitelist) {
- return kAllow;
+ // if policy is "just warn", always warn. We returned above for whitelist APIs.
+ if (policy == EnforcementPolicy::kJustWarn) {
+ return kAllowButWarn;
}
+ DCHECK(policy >= EnforcementPolicy::kDarkGreyAndBlackList);
// The logic below relies on equality of values in the enums EnforcementPolicy and
// HiddenApiAccessFlags::ApiList, and their ordering. Assertions are in hidden_api.cc.
if (static_cast<int>(policy) > static_cast<int>(api_list)) {
@@ -87,9 +102,18 @@
// is used as a helper when matching prefixes, and when logging the signature.
class MemberSignature {
private:
- std::string member_type_;
- std::vector<std::string> signature_parts_;
+ enum MemberType {
+ kField,
+ kMethod,
+ };
+
+ std::string class_name_;
+ std::string member_name_;
+ std::string type_signature_;
std::string tmp_;
+ MemberType type_;
+
+ inline std::vector<const char*> GetSignatureParts() const;
public:
explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -105,10 +129,15 @@
bool IsExempted(const std::vector<std::string>& exemptions);
void WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list);
+
+ void LogAccessToEventLog(AccessMethod access_method, Action action_taken);
};
template<typename T>
-Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method)
+Action GetMemberActionImpl(T* member,
+ HiddenApiAccessFlags::ApiList api_list,
+ Action action,
+ AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns true if the caller is either loaded by the boot strap class loader or comes from
@@ -143,7 +172,15 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
- Action action = GetActionFromAccessFlags(member->GetAccessFlags());
+ // Decode hidden API access flags.
+ // NB Multiple threads might try to access (and overwrite) these simultaneously,
+ // causing a race. We only do that if access has not been denied, so the race
+ // cannot change Java semantics. We should, however, decode the access flags
+ // once and use it throughout this function, otherwise we may get inconsistent
+ // results, e.g. print whitelist warnings (b/78327881).
+ HiddenApiAccessFlags::ApiList api_list = member->GetHiddenApiAccessFlags();
+
+ Action action = GetActionFromAccessFlags(member->GetHiddenApiAccessFlags());
if (action == kAllow) {
// Nothing to do.
return action;
@@ -157,7 +194,7 @@
}
// Member is hidden and caller is not in the platform.
- return detail::GetMemberActionImpl(member, action, access_method);
+ return detail::GetMemberActionImpl(member, api_list, action, access_method);
}
inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller)
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index 5a31dd4..68d4eec 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -18,10 +18,12 @@
#include "common_runtime_test.h"
#include "jni_internal.h"
+#include "proxy_test.h"
namespace art {
using hiddenapi::detail::MemberSignature;
+using hiddenapi::GetActionFromAccessFlags;
class HiddenApiTest : public CommonRuntimeTest {
protected:
@@ -30,7 +32,7 @@
CommonRuntimeTest::SetUp();
self_ = Thread::Current();
self_->TransitionFromSuspendedToRunnable();
- LoadDex("HiddenApiSignatures");
+ jclass_loader_ = LoadDex("HiddenApiSignatures");
bool started = runtime_->Start();
CHECK(started);
@@ -68,6 +70,7 @@
protected:
Thread* self_;
+ jobject jclass_loader_;
ArtField* class1_field1_;
ArtField* class1_field12_;
ArtMethod* class1_init_;
@@ -84,6 +87,44 @@
ArtMethod* class3_method1_i_;
};
+TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) {
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist), hiddenapi::kAllow);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist), hiddenapi::kAllow);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist), hiddenapi::kAllow);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist), hiddenapi::kAllow);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist),
+ hiddenapi::kAllow);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist),
+ hiddenapi::kAllowButWarn);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist),
+ hiddenapi::kAllowButWarn);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist),
+ hiddenapi::kAllowButWarn);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist),
+ hiddenapi::kAllow);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist),
+ hiddenapi::kAllowButWarn);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist),
+ hiddenapi::kDeny);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist),
+ hiddenapi::kDeny);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist),
+ hiddenapi::kAllow);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist),
+ hiddenapi::kAllowButWarn);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist),
+ hiddenapi::kAllowButWarnAndToast);
+ ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist),
+ hiddenapi::kDeny);
+}
+
TEST_F(HiddenApiTest, CheckMembersRead) {
ASSERT_NE(nullptr, class1_field1_);
ASSERT_NE(nullptr, class1_field12_);
@@ -272,4 +313,56 @@
ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
}
+TEST_F(HiddenApiTest, CheckMemberSignatureForProxyClass) {
+ ScopedObjectAccess soa(self_);
+ StackHandleScope<4> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader_)));
+
+ // Find interface we will create a proxy for.
+ Handle<mirror::Class> h_iface(hs.NewHandle(
+ class_linker_->FindClass(soa.Self(), "Lmypackage/packagea/Interface;", class_loader)));
+ ASSERT_TRUE(h_iface != nullptr);
+
+ // Create the proxy class.
+ std::vector<mirror::Class*> interfaces;
+ interfaces.push_back(h_iface.Get());
+ Handle<mirror::Class> proxyClass = hs.NewHandle(proxy_test::GenerateProxyClass(
+ soa, jclass_loader_, runtime_->GetClassLinker(), "$Proxy1234", interfaces));
+ ASSERT_TRUE(proxyClass != nullptr);
+ ASSERT_TRUE(proxyClass->IsProxyClass());
+ ASSERT_TRUE(proxyClass->IsInitialized());
+
+ // Find the "method" virtual method.
+ ArtMethod* method = nullptr;
+ for (auto& m : proxyClass->GetDeclaredVirtualMethods(kRuntimePointerSize)) {
+ if (strcmp("method", m.GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetName()) == 0) {
+ method = &m;
+ break;
+ }
+ }
+ ASSERT_TRUE(method != nullptr);
+
+ // Find the "interfaces" static field. This is generated for all proxies.
+ ArtField* field = nullptr;
+ for (size_t i = 0; i < proxyClass->NumStaticFields(); ++i) {
+ ArtField* f = proxyClass->GetStaticField(i);
+ if (strcmp("interfaces", f->GetName()) == 0) {
+ field = f;
+ break;
+ }
+ }
+ ASSERT_TRUE(field != nullptr);
+
+ // Test the signature. We expect the signature from the interface class.
+ std::ostringstream ss_method;
+ MemberSignature(method).Dump(ss_method);
+ ASSERT_EQ("Lmypackage/packagea/Interface;->method()V", ss_method.str());
+
+ // Test the signature. We expect the signature of the proxy class.
+ std::ostringstream ss_field;
+ MemberSignature(field).Dump(ss_field);
+ ASSERT_EQ("L$Proxy1234;->interfaces:[Ljava/lang/Class;", ss_field.str());
+}
+
} // namespace art
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 6d27cfe..4b8b891 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -252,6 +252,13 @@
void SetSaveProfilingInfo(bool save_profiling_info) {
profile_saver_options_.SetEnabled(save_profiling_info);
}
+ void SetWaitForJitNotificationsToSaveProfile(bool value) {
+ profile_saver_options_.SetWaitForJitNotificationsToSave(value);
+ }
+ void SetProfileAOTCode(bool value) {
+ profile_saver_options_.SetProfileAOTCode(value);
+ }
+
void SetJitAtFirstUse() {
use_jit_compilation_ = true;
compile_threshold_ = 0;
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 618fde8..efa8a4d 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -131,6 +131,11 @@
}
FetchAndCacheResolvedClassesAndMethods(/*startup*/ true);
+
+ // When we save without waiting for JIT notifications we use a simple
+ // exponential back off policy bounded by max_wait_without_jit.
+ uint32_t max_wait_without_jit = options_.GetMinSavePeriodMs() * 16;
+ uint64_t cur_wait_without_jit = options_.GetMinSavePeriodMs();
// Loop for the profiled methods.
while (!ShuttingDown(self)) {
uint64_t sleep_start = NanoTime();
@@ -138,7 +143,14 @@
uint64_t sleep_time = 0;
{
MutexLock mu(self, wait_lock_);
- period_condition_.Wait(self);
+ if (options_.GetWaitForJitNotificationsToSave()) {
+ period_condition_.Wait(self);
+ } else {
+ period_condition_.TimedWait(self, cur_wait_without_jit, 0);
+ if (cur_wait_without_jit < max_wait_without_jit) {
+ cur_wait_without_jit *= 2;
+ }
+ }
sleep_time = NanoTime() - sleep_start;
}
// Check if the thread was woken up for shutdown.
@@ -597,7 +609,13 @@
return nullptr;
}
-static bool ShouldProfileLocation(const std::string& location) {
+static bool ShouldProfileLocation(const std::string& location, bool profile_aot_code) {
+ if (profile_aot_code) {
+ // If we have to profile all the code, irrespective of its compilation state, return true
+ // right away.
+ return true;
+ }
+
OatFileManager& oat_manager = Runtime::Current()->GetOatFileManager();
const OatFile* oat_file = oat_manager.FindOpenedOatFileFromDexLocation(location);
if (oat_file == nullptr) {
@@ -629,7 +647,7 @@
std::vector<std::string> code_paths_to_profile;
for (const std::string& location : code_paths) {
- if (ShouldProfileLocation(location)) {
+ if (ShouldProfileLocation(location, options.GetProfileAOTCode())) {
code_paths_to_profile.push_back(location);
}
}
diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h
index d1e14e2..18f7899 100644
--- a/runtime/jit/profile_saver_options.h
+++ b/runtime/jit/profile_saver_options.h
@@ -41,7 +41,9 @@
min_notification_before_wake_(kMinNotificationBeforeWake),
max_notification_before_wake_(kMaxNotificationBeforeWake),
profile_path_(""),
- profile_boot_class_path_(false) {}
+ profile_boot_class_path_(false),
+ profile_aot_code_(false),
+ wait_for_jit_notifications_to_save_(true) {}
ProfileSaverOptions(
bool enabled,
@@ -53,7 +55,9 @@
uint32_t min_notification_before_wake,
uint32_t max_notification_before_wake,
const std::string& profile_path,
- bool profile_boot_class_path)
+ bool profile_boot_class_path,
+ bool profile_aot_code = false,
+ bool wait_for_jit_notifications_to_save = true)
: enabled_(enabled),
min_save_period_ms_(min_save_period_ms),
save_resolved_classes_delay_ms_(save_resolved_classes_delay_ms),
@@ -63,7 +67,9 @@
min_notification_before_wake_(min_notification_before_wake),
max_notification_before_wake_(max_notification_before_wake),
profile_path_(profile_path),
- profile_boot_class_path_(profile_boot_class_path) {}
+ profile_boot_class_path_(profile_boot_class_path),
+ profile_aot_code_(profile_aot_code),
+ wait_for_jit_notifications_to_save_(wait_for_jit_notifications_to_save) {}
bool IsEnabled() const {
return enabled_;
@@ -103,6 +109,18 @@
bool GetProfileBootClassPath() const {
return profile_boot_class_path_;
}
+ bool GetProfileAOTCode() const {
+ return profile_aot_code_;
+ }
+ void SetProfileAOTCode(bool value) {
+ profile_aot_code_ = value;
+ }
+ bool GetWaitForJitNotificationsToSave() const {
+ return wait_for_jit_notifications_to_save_;
+ }
+ void SetWaitForJitNotificationsToSave(bool value) {
+ wait_for_jit_notifications_to_save_ = value;
+ }
friend std::ostream & operator<<(std::ostream &os, const ProfileSaverOptions& pso) {
os << "enabled_" << pso.enabled_
@@ -113,7 +131,9 @@
<< ", min_classes_to_save_" << pso.min_classes_to_save_
<< ", min_notification_before_wake_" << pso.min_notification_before_wake_
<< ", max_notification_before_wake_" << pso.max_notification_before_wake_
- << ", profile_boot_class_path_" << pso.profile_boot_class_path_;
+ << ", profile_boot_class_path_" << pso.profile_boot_class_path_
+ << ", profile_aot_code_" << pso.profile_aot_code_
+ << ", wait_for_jit_notifications_to_save_" << pso.wait_for_jit_notifications_to_save_;
return os;
}
@@ -129,6 +149,8 @@
uint32_t max_notification_before_wake_;
std::string profile_path_;
bool profile_boot_class_path_;
+ bool profile_aot_code_;
+ bool wait_for_jit_notifications_to_save_;
};
} // namespace art
diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc
index 9126bea..2cb569c 100644
--- a/runtime/jit/profiling_info.cc
+++ b/runtime/jit/profiling_info.cc
@@ -26,12 +26,12 @@
namespace art {
ProfilingInfo::ProfilingInfo(ArtMethod* method, const std::vector<uint32_t>& entries)
- : number_of_inline_caches_(entries.size()),
- method_(method),
- is_method_being_compiled_(false),
- is_osr_method_being_compiled_(false),
+ : method_(method),
+ saved_entry_point_(nullptr),
+ number_of_inline_caches_(entries.size()),
current_inline_uses_(0),
- saved_entry_point_(nullptr) {
+ is_method_being_compiled_(false),
+ is_osr_method_being_compiled_(false) {
memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache));
for (size_t i = 0; i < number_of_inline_caches_; ++i) {
cache_[i].dex_pc_ = entries[i];
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index 788fa1f..a3dae83 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -132,28 +132,28 @@
private:
ProfilingInfo(ArtMethod* method, const std::vector<uint32_t>& entries);
- // Number of instructions we are profiling in the ArtMethod.
- const uint32_t number_of_inline_caches_;
-
// Method this profiling info is for.
// Not 'const' as JVMTI introduces obsolete methods that we implement by creating new ArtMethods.
// See JitCodeCache::MoveObsoleteMethod.
ArtMethod* method_;
+ // Entry point of the corresponding ArtMethod, while the JIT code cache
+ // is poking for the liveness of compiled code.
+ const void* saved_entry_point_;
+
+ // Number of instructions we are profiling in the ArtMethod.
+ const uint32_t number_of_inline_caches_;
+
+ // When the compiler inlines the method associated to this ProfilingInfo,
+ // it updates this counter so that the GC does not try to clear the inline caches.
+ uint16_t current_inline_uses_;
+
// Whether the ArtMethod is currently being compiled. This flag
// is implicitly guarded by the JIT code cache lock.
// TODO: Make the JIT code cache lock global.
bool is_method_being_compiled_;
bool is_osr_method_being_compiled_;
- // When the compiler inlines the method associated to this ProfilingInfo,
- // it updates this counter so that the GC does not try to clear the inline caches.
- uint16_t current_inline_uses_;
-
- // Entry point of the corresponding ArtMethod, while the JIT code cache
- // is poking for the liveness of compiled code.
- const void* saved_entry_point_;
-
// Dynamically allocated array of size `number_of_inline_caches_`.
InlineCache cache_[0];
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index a5ade6f..e88ff09 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -93,6 +93,10 @@
Runtime::Current()->SetHiddenApiExemptions(exemptions_vec);
}
+static void VMRuntime_setHiddenApiAccessLogSamplingRate(JNIEnv*, jclass, jint rate) {
+ Runtime::Current()->SetHiddenApiEventLogSampleRate(rate);
+}
+
static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass,
jint length) {
ScopedFastNativeObjectAccess soa(env);
@@ -678,6 +682,12 @@
#endif
}
+static void VMRuntime_setDedupeHiddenApiWarnings(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass klass ATTRIBUTE_UNUSED,
+ jboolean dedupe) {
+ Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe);
+}
+
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"),
NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
@@ -688,6 +698,7 @@
NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"),
NATIVE_METHOD(VMRuntime, hasUsedHiddenApi, "()Z"),
NATIVE_METHOD(VMRuntime, setHiddenApiExemptions, "([Ljava/lang/String;)V"),
+ NATIVE_METHOD(VMRuntime, setHiddenApiAccessLogSamplingRate, "(I)V"),
NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"),
FAST_NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"),
FAST_NATIVE_METHOD(VMRuntime, isNativeDebuggable, "()Z"),
@@ -718,6 +729,7 @@
NATIVE_METHOD(VMRuntime, getCurrentInstructionSet, "()Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, didPruneDalvikCache, "()Z"),
NATIVE_METHOD(VMRuntime, setSystemDaemonThreadPriority, "()V"),
+ NATIVE_METHOD(VMRuntime, setDedupeHiddenApiWarnings, "(Z)V"),
};
void register_dalvik_system_VMRuntime(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index cf0a72a..84c7926 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -363,6 +363,13 @@
<< "Child zygote processes should be forked with EnforcementPolicy::kDisable";
Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy);
Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings);
+ if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks &&
+ Runtime::Current()->GetHiddenApiEventLogSampleRate() != 0) {
+ // Hidden API checks are enabled, and we are sampling access for the event log. Initialize the
+ // random seed, to ensure the sampling is actually random. We do this post-fork, as doing it
+ // pre-fork would result in the same sequence for every forked process.
+ std::srand(static_cast<uint32_t>(NanoTime()));
+ }
// Clear the hidden API warning flag, in case it was set.
Runtime::Current()->SetPendingHiddenApiWarning(false);
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index bfd7f69..2c1c963 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -128,19 +128,20 @@
// the criteria. Some reflection calls only return public members
// (public_only == true), some members should be hidden from non-boot class path
// callers (enforce_hidden_api == true).
+template<typename T>
ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
bool enforce_hidden_api,
- uint32_t access_flags) {
- if (public_only && ((access_flags & kAccPublic) == 0)) {
+ T* member)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) {
return false;
}
- if (enforce_hidden_api &&
- hiddenapi::GetActionFromAccessFlags(access_flags) == hiddenapi::kDeny) {
- return false;
- }
-
- return true;
+ return hiddenapi::GetMemberAction(member,
+ nullptr,
+ [enforce_hidden_api] (Thread*) { return !enforce_hidden_api; },
+ hiddenapi::kNone)
+ != hiddenapi::kDeny;
}
ALWAYS_INLINE static inline ObjPtr<mirror::Class> DecodeClass(
@@ -269,12 +270,12 @@
bool enforce_hidden_api = ShouldEnforceHiddenApi(self);
// Lets go subtract all the non discoverable fields.
for (ArtField& field : ifields) {
- if (!IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+ if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
--array_size;
}
}
for (ArtField& field : sfields) {
- if (!IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+ if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
--array_size;
}
}
@@ -285,7 +286,7 @@
return nullptr;
}
for (ArtField& field : ifields) {
- if (IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+ if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
&field,
force_resolve);
@@ -300,7 +301,7 @@
}
}
for (ArtField& field : sfields) {
- if (IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+ if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
&field,
force_resolve);
@@ -521,7 +522,7 @@
DCHECK(m != nullptr);
return m->IsConstructor() &&
!m->IsStatic() &&
- IsDiscoverable(public_only, enforce_hidden_api, m->GetAccessFlags());
+ IsDiscoverable(public_only, enforce_hidden_api, m);
}
static jobjectArray Class_getDeclaredConstructorsInternal(
@@ -591,7 +592,7 @@
uint32_t modifiers = m.GetAccessFlags();
// Add non-constructor declared methods.
if ((modifiers & kAccConstructor) == 0 &&
- IsDiscoverable(public_only, enforce_hidden_api, modifiers)) {
+ IsDiscoverable(public_only, enforce_hidden_api, &m)) {
++num_methods;
}
}
@@ -605,7 +606,7 @@
for (ArtMethod& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
uint32_t modifiers = m.GetAccessFlags();
if ((modifiers & kAccConstructor) == 0 &&
- IsDiscoverable(public_only, enforce_hidden_api, modifiers)) {
+ IsDiscoverable(public_only, enforce_hidden_api, &m)) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
auto* method =
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index 3ad3a4b..4e0bf89 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -18,97 +18,16 @@
#include <vector>
#include "art_field-inl.h"
-#include "art_method-inl.h"
#include "base/enums.h"
-#include "class_linker-inl.h"
#include "common_compiler_test.h"
-#include "mirror/class-inl.h"
#include "mirror/field-inl.h"
-#include "mirror/method.h"
+#include "proxy_test.h"
#include "scoped_thread_state_change-inl.h"
namespace art {
+namespace proxy_test {
-class ProxyTest : public CommonCompilerTest {
- public:
- // Generate a proxy class with the given name and interfaces. This is a simplification from what
- // libcore does to fit to our test needs. We do not check for duplicated interfaces or methods and
- // we do not declare exceptions.
- mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, jobject jclass_loader,
- const char* className,
- const std::vector<mirror::Class*>& interfaces)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- mirror::Class* javaLangObject = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
- CHECK(javaLangObject != nullptr);
-
- jclass javaLangClass = soa.AddLocalReference<jclass>(mirror::Class::GetJavaLangClass());
-
- // Builds the interfaces array.
- jobjectArray proxyClassInterfaces = soa.Env()->NewObjectArray(interfaces.size(), javaLangClass,
- nullptr);
- soa.Self()->AssertNoPendingException();
- for (size_t i = 0; i < interfaces.size(); ++i) {
- soa.Env()->SetObjectArrayElement(proxyClassInterfaces, i,
- soa.AddLocalReference<jclass>(interfaces[i]));
- }
-
- // Builds the method array.
- jsize methods_count = 3; // Object.equals, Object.hashCode and Object.toString.
- for (mirror::Class* interface : interfaces) {
- methods_count += interface->NumVirtualMethods();
- }
- jobjectArray proxyClassMethods = soa.Env()->NewObjectArray(
- methods_count, soa.AddLocalReference<jclass>(mirror::Method::StaticClass()), nullptr);
- soa.Self()->AssertNoPendingException();
-
- jsize array_index = 0;
- // Fill the method array
- DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
- ArtMethod* method = javaLangObject->FindClassMethod(
- "equals", "(Ljava/lang/Object;)Z", kRuntimePointerSize);
- CHECK(method != nullptr);
- CHECK(!method->IsDirect());
- CHECK(method->GetDeclaringClass() == javaLangObject);
- DCHECK(!Runtime::Current()->IsActiveTransaction());
- soa.Env()->SetObjectArrayElement(
- proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
- mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
- method = javaLangObject->FindClassMethod("hashCode", "()I", kRuntimePointerSize);
- CHECK(method != nullptr);
- CHECK(!method->IsDirect());
- CHECK(method->GetDeclaringClass() == javaLangObject);
- soa.Env()->SetObjectArrayElement(
- proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
- mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
- method = javaLangObject->FindClassMethod(
- "toString", "()Ljava/lang/String;", kRuntimePointerSize);
- CHECK(method != nullptr);
- CHECK(!method->IsDirect());
- CHECK(method->GetDeclaringClass() == javaLangObject);
- soa.Env()->SetObjectArrayElement(
- proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
- mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
- // Now adds all interfaces virtual methods.
- for (mirror::Class* interface : interfaces) {
- for (auto& m : interface->GetDeclaredVirtualMethods(kRuntimePointerSize)) {
- soa.Env()->SetObjectArrayElement(
- proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
- mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), &m)));
- }
- }
- CHECK_EQ(array_index, methods_count);
-
- // Builds an empty exception array.
- jobjectArray proxyClassThrows = soa.Env()->NewObjectArray(0, javaLangClass, nullptr);
- soa.Self()->AssertNoPendingException();
-
- mirror::Class* proxyClass = class_linker_->CreateProxyClass(
- soa, soa.Env()->NewStringUTF(className), proxyClassInterfaces, jclass_loader,
- proxyClassMethods, proxyClassThrows);
- soa.Self()->AssertNoPendingException();
- return proxyClass;
- }
-};
+class ProxyTest : public CommonRuntimeTest {};
// Creates a proxy class and check ClassHelper works correctly.
TEST_F(ProxyTest, ProxyClassHelper) {
@@ -129,7 +48,7 @@
interfaces.push_back(I.Get());
interfaces.push_back(J.Get());
Handle<mirror::Class> proxy_class(hs.NewHandle(
- GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces)));
+ GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1234", interfaces)));
interfaces.clear(); // Don't least possibly stale objects in the array as good practice.
ASSERT_TRUE(proxy_class != nullptr);
ASSERT_TRUE(proxy_class->IsProxyClass());
@@ -164,7 +83,8 @@
std::vector<mirror::Class*> interfaces;
interfaces.push_back(I.Get());
interfaces.push_back(J.Get());
- proxyClass = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces));
+ proxyClass = hs.NewHandle(
+ GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1234", interfaces));
}
ASSERT_TRUE(proxyClass != nullptr);
@@ -212,8 +132,10 @@
Handle<mirror::Class> proxyClass1;
{
std::vector<mirror::Class*> interfaces;
- proxyClass0 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy0", interfaces));
- proxyClass1 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1", interfaces));
+ proxyClass0 = hs.NewHandle(
+ GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy0", interfaces));
+ proxyClass1 = hs.NewHandle(
+ GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1", interfaces));
}
ASSERT_TRUE(proxyClass0 != nullptr);
@@ -255,4 +177,5 @@
EXPECT_EQ(field11->GetArtField(), &static_fields1->At(1));
}
+} // namespace proxy_test
} // namespace art
diff --git a/runtime/proxy_test.h b/runtime/proxy_test.h
new file mode 100644
index 0000000..b559823
--- /dev/null
+++ b/runtime/proxy_test.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 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_PROXY_TEST_H_
+#define ART_RUNTIME_PROXY_TEST_H_
+
+#include <jni.h>
+#include <vector>
+
+#include "art_method-inl.h"
+#include "class_linker-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/method.h"
+
+namespace art {
+namespace proxy_test {
+
+// Generate a proxy class with the given name and interfaces. This is a simplification from what
+// libcore does to fit to our test needs. We do not check for duplicated interfaces or methods and
+// we do not declare exceptions.
+mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa,
+ jobject jclass_loader,
+ ClassLinker* class_linker,
+ const char* className,
+ const std::vector<mirror::Class*>& interfaces)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class* javaLangObject = class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
+ CHECK(javaLangObject != nullptr);
+
+ jclass javaLangClass = soa.AddLocalReference<jclass>(mirror::Class::GetJavaLangClass());
+
+ // Builds the interfaces array.
+ jobjectArray proxyClassInterfaces = soa.Env()->NewObjectArray(interfaces.size(), javaLangClass,
+ nullptr);
+ soa.Self()->AssertNoPendingException();
+ for (size_t i = 0; i < interfaces.size(); ++i) {
+ soa.Env()->SetObjectArrayElement(proxyClassInterfaces, i,
+ soa.AddLocalReference<jclass>(interfaces[i]));
+ }
+
+ // Builds the method array.
+ jsize methods_count = 3; // Object.equals, Object.hashCode and Object.toString.
+ for (mirror::Class* interface : interfaces) {
+ methods_count += interface->NumVirtualMethods();
+ }
+ jobjectArray proxyClassMethods = soa.Env()->NewObjectArray(
+ methods_count, soa.AddLocalReference<jclass>(mirror::Method::StaticClass()), nullptr);
+ soa.Self()->AssertNoPendingException();
+
+ jsize array_index = 0;
+ // Fill the method array
+ DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
+ ArtMethod* method = javaLangObject->FindClassMethod(
+ "equals", "(Ljava/lang/Object;)Z", kRuntimePointerSize);
+ CHECK(method != nullptr);
+ CHECK(!method->IsDirect());
+ CHECK(method->GetDeclaringClass() == javaLangObject);
+ DCHECK(!Runtime::Current()->IsActiveTransaction());
+ soa.Env()->SetObjectArrayElement(
+ proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
+ mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
+ method = javaLangObject->FindClassMethod("hashCode", "()I", kRuntimePointerSize);
+ CHECK(method != nullptr);
+ CHECK(!method->IsDirect());
+ CHECK(method->GetDeclaringClass() == javaLangObject);
+ soa.Env()->SetObjectArrayElement(
+ proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
+ mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
+ method = javaLangObject->FindClassMethod(
+ "toString", "()Ljava/lang/String;", kRuntimePointerSize);
+ CHECK(method != nullptr);
+ CHECK(!method->IsDirect());
+ CHECK(method->GetDeclaringClass() == javaLangObject);
+ soa.Env()->SetObjectArrayElement(
+ proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
+ mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
+ // Now adds all interfaces virtual methods.
+ for (mirror::Class* interface : interfaces) {
+ for (auto& m : interface->GetDeclaredVirtualMethods(kRuntimePointerSize)) {
+ soa.Env()->SetObjectArrayElement(
+ proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
+ mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), &m)));
+ }
+ }
+ CHECK_EQ(array_index, methods_count);
+
+ // Builds an empty exception array.
+ jobjectArray proxyClassThrows = soa.Env()->NewObjectArray(0, javaLangClass, nullptr);
+ soa.Self()->AssertNoPendingException();
+
+ mirror::Class* proxyClass = class_linker->CreateProxyClass(
+ soa, soa.Env()->NewStringUTF(className), proxyClassInterfaces, jclass_loader,
+ proxyClassMethods, proxyClassThrows);
+ soa.Self()->AssertNoPendingException();
+ return proxyClass;
+}
+
+} // namespace proxy_test
+} // namespace art
+
+#endif // ART_RUNTIME_PROXY_TEST_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 7823014..e2e315c 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -273,6 +273,7 @@
pending_hidden_api_warning_(false),
dedupe_hidden_api_warnings_(true),
always_set_hidden_api_warning_flag_(false),
+ hidden_api_access_event_log_rate_(0),
dump_native_stack_on_sig_quit_(true),
pruned_dalvik_cache_(false),
// Initially assume we perceive jank in case the process state is never updated.
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 56d95e0..87f5b51 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -569,6 +569,14 @@
return always_set_hidden_api_warning_flag_;
}
+ void SetHiddenApiEventLogSampleRate(uint32_t rate) {
+ hidden_api_access_event_log_rate_ = rate;
+ }
+
+ uint32_t GetHiddenApiEventLogSampleRate() const {
+ return hidden_api_access_event_log_rate_;
+ }
+
bool IsDexFileFallbackEnabled() const {
return allow_dex_file_fallback_;
}
@@ -1006,7 +1014,8 @@
// Whether access checks on hidden API should be performed.
hiddenapi::EnforcementPolicy hidden_api_policy_;
- // List of signature prefixes of methods that have been removed from the blacklist
+ // List of signature prefixes of methods that have been removed from the blacklist, and treated
+ // as if whitelisted.
std::vector<std::string> hidden_api_exemptions_;
// Whether the application has used an API which is not restricted but we
@@ -1022,6 +1031,10 @@
// when there is a warning. This is only used for testing.
bool always_set_hidden_api_warning_flag_;
+ // How often to log hidden API access to the event log. An integer between 0 (never)
+ // and 0x10000 (always).
+ uint32_t hidden_api_access_event_log_rate_;
+
// Whether threads should dump their native stack on SIGQUIT.
bool dump_native_stack_on_sig_quit_;
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 72292c3..bc29aac 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3801,16 +3801,8 @@
must_fail = true;
// Try to find the method also with the other type for better error reporting below
// but do not store such bogus lookup result in the DexCache or VerifierDeps.
- if (klass->IsInterface()) {
- // NB This is normally not really allowed but we want to get any static or private object
- // methods for error message purposes. This will never be returned.
- // TODO We might want to change the verifier to not require this.
- res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size);
- } else {
- // If there was an interface method with the same signature,
- // we would have found it also in the "copied" methods.
- DCHECK(klass->FindInterfaceMethod(dex_cache_.Get(), dex_method_idx, pointer_size) == nullptr);
- }
+ res_method = class_linker->FindIncompatibleMethod(
+ klass, dex_cache_.Get(), class_loader_.Get(), dex_method_idx);
}
if (res_method == nullptr) {
diff --git a/test/674-hiddenapi/src-art/Main.java b/test/674-hiddenapi/src-art/Main.java
index a808e94..782748c 100644
--- a/test/674-hiddenapi/src-art/Main.java
+++ b/test/674-hiddenapi/src-art/Main.java
@@ -16,6 +16,7 @@
import dalvik.system.InMemoryDexClassLoader;
import dalvik.system.PathClassLoader;
+import dalvik.system.VMRuntime;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Constructor;
@@ -34,26 +35,41 @@
// Enable hidden API checks in case they are disabled by default.
init();
+ // TODO there are sequential depencies between these test cases, and bugs
+ // in the production code may lead to subsequent tests to erroneously pass,
+ // or test the wrong thing. We rely on not deduping hidden API warnings
+ // here for the same reasons), meaning the code under test and production
+ // code are running in different configurations. Each test should be run in
+ // a fresh process to ensure that they are working correcting and not
+ // accidentally interfering with eachother.
+
// Run test with both parent and child dex files loaded with class loaders.
// The expectation is that hidden members in parent should be visible to
// the child.
- doTest(false, false);
+ doTest(false, false, false);
doUnloading();
// Now append parent dex file to boot class path and run again. This time
- // the child dex file should not be able to access private APIs of the parent.
+ // the child dex file should not be able to access private APIs of the
+ // parent.
appendToBootClassLoader(DEX_PARENT_BOOT);
- doTest(true, false);
+ doTest(true, false, false);
+ doUnloading();
+
+ // Now run the same test again, but with the blacklist exmemptions list set
+ // to "L" which matches everything.
+ doTest(true, false, true);
doUnloading();
// And finally append to child to boot class path as well. With both in the
// boot class path, access should be granted.
appendToBootClassLoader(DEX_CHILD);
- doTest(true, true);
+ doTest(true, true, false);
doUnloading();
}
- private static void doTest(boolean parentInBoot, boolean childInBoot) throws Exception {
+ private static void doTest(boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis)
+ throws Exception {
// Load parent dex if it is not in boot class path.
ClassLoader parentLoader = null;
if (parentInBoot) {
@@ -78,12 +94,18 @@
// be loaded once, but for some reason even classes from a class loader
// cannot register their native methods against symbols in a shared library
// loaded by their parent class loader.
- String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot);
+ String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot, whitelistAllApis);
+
+ if (whitelistAllApis) {
+ VMRuntime.getRuntime().setHiddenApiExemptions(new String[]{"L"});
+ }
// Invoke ChildClass.runTest
Class.forName("ChildClass", true, childLoader)
- .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE)
- .invoke(null, nativeLibCopy, parentInBoot, childInBoot);
+ .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE)
+ .invoke(null, nativeLibCopy, parentInBoot, childInBoot, whitelistAllApis);
+
+ VMRuntime.getRuntime().setHiddenApiExemptions(new String[0]);
}
// Routine which tries to figure out the absolute path of our native library.
@@ -122,20 +144,21 @@
return buffer;
}
- // Copy native library to a new file with a unique name so it does not conflict
- // with other loaded instance of the same binary file.
- private static String createNativeLibCopy(boolean parentInBoot, boolean childInBoot)
- throws Exception {
+ // Copy native library to a new file with a unique name so it does not
+ // conflict with other loaded instance of the same binary file.
+ private static String createNativeLibCopy(
+ boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis) throws Exception {
String tempFileName = System.mapLibraryName(
- "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0"));
+ "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0") +
+ (whitelistAllApis ? "1" : "0"));
File tempFile = new File(System.getenv("DEX_LOCATION"), tempFileName);
Files.copy(new File(nativeLibFileName).toPath(), tempFile.toPath());
return tempFile.getAbsolutePath();
}
private static void doUnloading() {
- // Do multiple GCs to prevent rare flakiness if some other thread is keeping the
- // classloader live.
+ // Do multiple GCs to prevent rare flakiness if some other thread is
+ // keeping the classloader live.
for (int i = 0; i < 5; ++i) {
Runtime.getRuntime().gc();
}
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
index ea66f16..0349e8f 100644
--- a/test/674-hiddenapi/src-ex/ChildClass.java
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -70,7 +70,7 @@
private static final boolean booleanValues[] = new boolean[] { false, true };
public static void runTest(String libFileName, boolean expectedParentInBoot,
- boolean expectedChildInBoot) throws Exception {
+ boolean expectedChildInBoot, boolean everythingWhitelisted) throws Exception {
System.load(libFileName);
// Check expectations about loading into boot class path.
@@ -84,13 +84,14 @@
throw new RuntimeException("Expected ChildClass " + (expectedChildInBoot ? "" : "not ") +
"in boot class path");
}
+ ChildClass.everythingWhitelisted = everythingWhitelisted;
boolean isSameBoot = (isParentInBoot == isChildInBoot);
// Run meaningful combinations of access flags.
for (Hiddenness hiddenness : Hiddenness.values()) {
final Behaviour expected;
- if (isSameBoot || hiddenness == Hiddenness.Whitelist) {
+ if (isSameBoot || hiddenness == Hiddenness.Whitelist || everythingWhitelisted) {
expected = Behaviour.Granted;
} else if (hiddenness == Hiddenness.Blacklist) {
expected = Behaviour.Denied;
@@ -121,6 +122,7 @@
checkLinking("LinkFieldGet" + suffix, /*takesParameter*/ false, expected);
checkLinking("LinkFieldSet" + suffix, /*takesParameter*/ true, expected);
checkLinking("LinkMethod" + suffix, /*takesParameter*/ false, expected);
+ checkLinking("LinkMethodInterface" + suffix, /*takesParameter*/ false, expected);
}
// Check whether Class.newInstance succeeds.
@@ -509,14 +511,16 @@
String fn, boolean canAccess) {
throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
"." + name + " to " + (canAccess ? "" : "not ") + "be discoverable with " + fn + ". " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
+ "everythingWhitelisted = " + everythingWhitelisted);
}
private static void throwAccessException(Class<?> klass, String name, boolean isField,
String fn) {
throw new RuntimeException("Expected to be able to access " + (isField ? "field " : "method ") +
klass.getName() + "." + name + " using " + fn + ". " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
+ "everythingWhitelisted = " + everythingWhitelisted);
}
private static void throwWarningException(Class<?> klass, String name, boolean isField,
@@ -524,7 +528,8 @@
throw new RuntimeException("Expected access to " + (isField ? "field " : "method ") +
klass.getName() + "." + name + " using " + fn + " to " + (setsWarning ? "" : "not ") +
"set the warning flag. " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
+ "everythingWhitelisted = " + everythingWhitelisted);
}
private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
@@ -534,6 +539,7 @@
private static boolean isParentInBoot;
private static boolean isChildInBoot;
+ private static boolean everythingWhitelisted;
private static native boolean hasPendingWarning();
private static native void clearWarning();
diff --git a/test/674-hiddenapi/src-ex/Linking.java b/test/674-hiddenapi/src-ex/Linking.java
index a89b92b..0fa0b19 100644
--- a/test/674-hiddenapi/src-ex/Linking.java
+++ b/test/674-hiddenapi/src-ex/Linking.java
@@ -174,6 +174,32 @@
}
}
+// INVOKE INSTANCE INTERFACE METHOD
+
+class LinkMethodInterfaceWhitelist {
+ public static int access() {
+ return DummyClass.getInterfaceInstance().methodPublicWhitelist();
+ }
+}
+
+class LinkMethodInterfaceLightGreylist {
+ public static int access() {
+ return DummyClass.getInterfaceInstance().methodPublicLightGreylist();
+ }
+}
+
+class LinkMethodInterfaceDarkGreylist {
+ public static int access() {
+ return DummyClass.getInterfaceInstance().methodPublicDarkGreylist();
+ }
+}
+
+class LinkMethodInterfaceBlacklist {
+ public static int access() {
+ return DummyClass.getInterfaceInstance().methodPublicBlacklist();
+ }
+}
+
// INVOKE STATIC METHOD
class LinkMethodStaticWhitelist {
@@ -199,3 +225,29 @@
return ParentClass.methodPublicStaticBlacklist();
}
}
+
+// INVOKE INTERFACE STATIC METHOD
+
+class LinkMethodInterfaceStaticWhitelist {
+ public static int access() {
+ return ParentInterface.methodPublicStaticWhitelist();
+ }
+}
+
+class LinkMethodInterfaceStaticLightGreylist {
+ public static int access() {
+ return ParentInterface.methodPublicStaticLightGreylist();
+ }
+}
+
+class LinkMethodInterfaceStaticDarkGreylist {
+ public static int access() {
+ return ParentInterface.methodPublicStaticDarkGreylist();
+ }
+}
+
+class LinkMethodInterfaceStaticBlacklist {
+ public static int access() {
+ return ParentInterface.methodPublicStaticBlacklist();
+ }
+}
diff --git a/test/674-hiddenapi/src/DummyClass.java b/test/674-hiddenapi/src/DummyClass.java
new file mode 100644
index 0000000..51281a2
--- /dev/null
+++ b/test/674-hiddenapi/src/DummyClass.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.
+ */
+
+public class DummyClass implements ParentInterface {
+ public int methodPublicWhitelist() { return 1; }
+ public int methodPublicLightGreylist() { return 2; }
+ public int methodPublicDarkGreylist() { return 3; }
+ public int methodPublicBlacklist() { return 4; }
+
+ public static ParentInterface getInterfaceInstance() {
+ return new DummyClass();
+ }
+}
diff --git a/test/674-hiddenapi/src/ParentInterface.java b/test/674-hiddenapi/src/ParentInterface.java
index e36fe0e..f79ac9d 100644
--- a/test/674-hiddenapi/src/ParentInterface.java
+++ b/test/674-hiddenapi/src/ParentInterface.java
@@ -23,9 +23,9 @@
// INSTANCE METHOD
int methodPublicWhitelist();
- int methodPublicBlacklist();
int methodPublicLightGreylist();
int methodPublicDarkGreylist();
+ int methodPublicBlacklist();
// STATIC METHOD
static int methodPublicStaticWhitelist() { return 21; }
diff --git a/test/HiddenApiSignatures/Interface.java b/test/HiddenApiSignatures/Interface.java
new file mode 100644
index 0000000..f141d09
--- /dev/null
+++ b/test/HiddenApiSignatures/Interface.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package mypackage.packagea;
+
+public interface Interface {
+ public void method();
+}