Simplify hidden_api.h logic

Refactor GetMemberAction to return a boolean whether access to a class
member should be denied. This also moves StrictMode consumer
notification into hidden_api.cc and removes notifications for toasts.
Tests are changed accordingly.

Test: phone boots
Test: m test-art
Merged-In: I02902143de0ff91d402ba79c83f28226b1822a6f
Change-Id: I02902143de0ff91d402ba79c83f28226b1822a6f
(cherry picked from commit 51995f90adaa0e5047dee56d22f15e4225e70517)
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 57f1a59..ed00e2a 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -32,11 +32,10 @@
 // This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in
 // frameworks/base/core/java/android/content/pm/ApplicationInfo.java
 enum class EnforcementPolicy {
-  kNoChecks             = 0,
+  kDisabled             = 0,
   kJustWarn             = 1,  // keep checks enabled, but allow everything (enables logging)
-  kDarkGreyAndBlackList = 2,  // ban dark grey & blacklist
-  kBlacklistOnly        = 3,  // ban blacklist violations only
-  kMax = kBlacklistOnly,
+  kEnabled              = 2,  // ban dark grey & blacklist
+  kMax = kEnabled,
 };
 
 inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) {
@@ -45,56 +44,59 @@
   return static_cast<EnforcementPolicy>(api_policy_int);
 }
 
-enum Action {
-  kAllow,
-  kAllowButWarn,
-  kAllowButWarnAndToast,
-  kDeny
-};
-
-enum AccessMethod {
+enum class AccessMethod {
   kNone,  // internal test that does not correspond to an actual access by app
   kReflection,
   kJNI,
   kLinking,
 };
 
-// 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,
+struct AccessContext {
+ public:
+  explicit AccessContext(bool is_trusted) : is_trusted_(is_trusted) {}
+
+  explicit AccessContext(ObjPtr<mirror::Class> klass) : is_trusted_(GetIsTrusted(klass)) {}
+
+  AccessContext(ObjPtr<mirror::ClassLoader> class_loader, ObjPtr<mirror::DexCache> dex_cache)
+      : is_trusted_(GetIsTrusted(class_loader, dex_cache)) {}
+
+  bool IsTrusted() const { return is_trusted_; }
+
+ private:
+  static bool GetIsTrusted(ObjPtr<mirror::ClassLoader> class_loader,
+                           ObjPtr<mirror::DexCache> dex_cache)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    // Trust if the caller is in is boot class loader.
+    if (class_loader.IsNull()) {
+      return true;
+    }
+
+    // Trust if caller is in a platform dex file.
+    if (!dex_cache.IsNull()) {
+      const DexFile* dex_file = dex_cache->GetDexFile();
+      if (dex_file != nullptr && dex_file->IsPlatformDexFile()) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  static bool GetIsTrusted(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(!klass.IsNull());
+
+    if (klass->ShouldSkipHiddenApiChecks() && Runtime::Current()->IsJavaDebuggable()) {
+      // Class is known, it is marked trusted and we are in debuggable mode.
+      return true;
+    }
+
+    // Check other aspects of the context.
+    return GetIsTrusted(klass->GetClassLoader(), klass->GetDexCache());
+  }
+
+  bool is_trusted_;
 };
 
-inline Action GetActionFromAccessFlags(ApiList api_list) {
-  if (api_list == ApiList::kWhitelist) {
-    return kAllow;
-  }
-
-  EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
-  if (policy == EnforcementPolicy::kNoChecks) {
-    // Exit early. Nothing to enforce.
-    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
-  // ApiList, and their ordering. Assertions are in hidden_api.cc.
-  if (static_cast<int>(policy) > static_cast<int>(api_list)) {
-    return api_list == ApiList::kDarkGreylist
-        ? kAllowButWarnAndToast
-        : kAllowButWarn;
-  } else {
-    return kDeny;
-  }
-}
-
 class ScopedHiddenApiEnforcementPolicySetting {
  public:
   explicit ScopedHiddenApiEnforcementPolicySetting(EnforcementPolicy new_policy)
@@ -114,6 +116,13 @@
 // Implementation details. DO NOT ACCESS DIRECTLY.
 namespace detail {
 
+enum class SdkCodes {
+  kVersionNone      = std::numeric_limits<int32_t>::min(),
+  kVersionUnlimited = std::numeric_limits<int32_t>::max(),
+  kVersionO_MR1     = 27,
+  kVersionP         = 28,
+};
+
 // Class to encapsulate the signature of a member (ArtField or ArtMethod). This
 // is used as a helper when matching prefixes, and when logging the signature.
 class MemberSignature {
@@ -146,59 +155,31 @@
 
   void WarnAboutAccess(AccessMethod access_method, ApiList list);
 
-  void LogAccessToEventLog(AccessMethod access_method, Action action_taken);
+  void LogAccessToEventLog(AccessMethod access_method, bool access_denied);
+
+  // Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that
+  // |member| was accessed. This is usually called when an API is on the black,
+  // dark grey or light grey lists. Given that the callback can execute arbitrary
+  // code, a call to this method can result in thread suspension.
+  void NotifyHiddenApiListener(AccessMethod access_method);
 };
 
 template<typename T>
-Action GetMemberActionImpl(T* member,
-                           ApiList api_list,
-                           Action action,
-                           AccessMethod access_method)
+bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, 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
-// a dex file located in ${ANDROID_ROOT}/framework/.
-ALWAYS_INLINE
-inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller,
-                            ObjPtr<mirror::ClassLoader> caller_class_loader,
-                            ObjPtr<mirror::DexCache> caller_dex_cache)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (caller_class_loader.IsNull()) {
-    // Boot class loader.
-    return true;
-  }
-
-  if (!caller_dex_cache.IsNull()) {
-    const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
-    if (caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile()) {
-      // Caller is in a platform dex file.
-      return true;
-    }
-  }
-
-  if (!caller.IsNull() &&
-      caller->ShouldSkipHiddenApiChecks() &&
-      Runtime::Current()->IsJavaDebuggable()) {
-    // We are in debuggable mode and this caller has been marked trusted.
-    return true;
-  }
-
-  return false;
-}
-
 }  // namespace detail
 
-// Returns true if access to `member` should be denied to the caller of the
-// reflective query. The decision is based on whether the caller is trusted or
-// not. Because different users of this function determine this in a different
-// way, `fn_caller_is_trusted(self)` is called and should return true if the
-// caller is allowed to access the platform.
+// Returns true if access to `member` should be denied in the given context.
+// The decision is based on whether the caller is in a trusted context or not.
+// Because determining the access context can be expensive, a lambda function
+// "fn_get_access_context" is lazily invoked after other criteria have been
+// considered.
 // This function might print warnings into the log if the member is hidden.
 template<typename T>
-inline Action GetMemberAction(T* member,
-                              Thread* self,
-                              std::function<bool(Thread*)> fn_caller_is_trusted,
-                              AccessMethod access_method)
+inline bool ShouldDenyAccessToMember(T* member,
+                                     std::function<AccessContext()> fn_get_access_context,
+                                     AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(member != nullptr);
 
@@ -210,53 +191,34 @@
   // results, e.g. print whitelist warnings (b/78327881).
   ApiList api_list = member->GetHiddenApiAccessFlags();
 
-  Action action = GetActionFromAccessFlags(member->GetHiddenApiAccessFlags());
-  if (action == kAllow) {
-    // Nothing to do.
-    return action;
+  // Exit early if member is on the whitelist.
+  if (api_list == ApiList::kWhitelist) {
+    return false;
   }
 
-  // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access.
+  // Check if caller is exempted from access checks.
   // This can be *very* expensive. Save it for last.
-  if (fn_caller_is_trusted(self)) {
-    // Caller is trusted. Exit.
-    return kAllow;
+  if (fn_get_access_context().IsTrusted()) {
+    return false;
   }
 
-  // Member is hidden and caller is not in the platform.
-  return detail::GetMemberActionImpl(member, api_list, action, access_method);
+  // Member is hidden and caller is not exempted. Enter slow path.
+  return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
 }
 
-inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller) REQUIRES_SHARED(Locks::mutator_lock_) {
-  return !caller.IsNull() &&
-      detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache());
-}
-
-// Returns true if access to `member` should be denied to a caller loaded with
-// `caller_class_loader`.
-// This function might print warnings into the log if the member is hidden.
+// Helper method for callers where access context can be determined beforehand.
+// Wraps AccessContext in a lambda and passes it to the real ShouldDenyAccessToMember.
 template<typename T>
-inline Action GetMemberAction(T* member,
-                              ObjPtr<mirror::ClassLoader> caller_class_loader,
-                              ObjPtr<mirror::DexCache> caller_dex_cache,
-                              AccessMethod access_method)
+inline bool ShouldDenyAccessToMember(T* member,
+                                     AccessContext access_context,
+                                     AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  bool is_caller_trusted =
-      detail::IsCallerTrusted(/* caller= */ nullptr, caller_class_loader, caller_dex_cache);
-  return GetMemberAction(member,
-                         /* thread= */ nullptr,
-                         [is_caller_trusted] (Thread*) { return is_caller_trusted; },
-                         access_method);
+  return ShouldDenyAccessToMember(
+      member,
+      [&] () REQUIRES_SHARED(Locks::mutator_lock_) { return access_context; },
+      access_method);
 }
 
-// Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that
-// |member| was accessed. This is usually called when an API is on the black,
-// dark grey or light grey lists. Given that the callback can execute arbitrary
-// code, a call to this method can result in thread suspension.
-template<typename T> void NotifyHiddenApiListener(T* member)
-    REQUIRES_SHARED(Locks::mutator_lock_);
-
-
 }  // namespace hiddenapi
 }  // namespace art