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();
+}