More flexible API enforcement policy support.

This CL adds the ability to configure which banned API lists to enforce,
defined by new enum hiddenapi::ApiEnforcementPolicy. Currently, the policy
can be set at zygote fork time, but not at dex optimization time where
blacklist enforcement is still assumed. As such, making the policy more
strict will not work as expected yet. This will be improved in a follow up
CL.

Test: art tests pass
Test: Device boots

BUG: 73337509

(cherry-picked from commit 159f596eec01adbb5a1c9654402c137cdb943131)

Change-Id: I6c319bb8a3000cb1d3c4693b4fb196e749c36d96
Merged-In: I33f9afce628a86727e400052f4d5979d3536da8c
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index f2ea2fd..321d55d 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -27,6 +27,23 @@
 namespace art {
 namespace hiddenapi {
 
+// Hidden API enforcement policy
+// This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in
+// frameworks/base/core/java/android/content/pm/ApplicationInfo.java
+enum class EnforcementPolicy {
+  kNoChecks             = 0,
+  kAllLists             = 1,  // ban anything but whitelist
+  kDarkGreyAndBlackList = 2,  // ban dark grey & blacklist
+  kBlacklistOnly        = 3,  // ban blacklist violations only
+  kMax = kBlacklistOnly,
+};
+
+inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) {
+  DCHECK_GE(api_policy_int, 0);
+  DCHECK_LE(api_policy_int, static_cast<int>(EnforcementPolicy::kMax));
+  return static_cast<EnforcementPolicy>(api_policy_int);
+}
+
 enum Action {
   kAllow,
   kAllowButWarn,
@@ -59,16 +76,38 @@
   return os;
 }
 
+static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) {
+  return static_cast<int>(policy) == static_cast<int>(apiList);
+}
+
 inline Action GetMemberAction(uint32_t access_flags) {
-  switch (HiddenApiAccessFlags::DecodeFromRuntime(access_flags)) {
-    case HiddenApiAccessFlags::kWhitelist:
-      return kAllow;
-    case HiddenApiAccessFlags::kLightGreylist:
-      return kAllowButWarn;
-    case HiddenApiAccessFlags::kDarkGreylist:
-      return kAllowButWarnAndToast;
-    case HiddenApiAccessFlags::kBlacklist:
-      return kDeny;
+  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;
+  }
+  // The logic below relies on equality of values in the enums EnforcementPolicy and
+  // HiddenApiAccessFlags::ApiList, and their ordering. Assert that this is as expected.
+  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::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
+      "EnforcementPolicy values ordering not correct");
+  if (static_cast<int>(policy) > static_cast<int>(api_list)) {
+    return api_list == HiddenApiAccessFlags::kDarkGreylist
+        ? kAllowButWarnAndToast
+        : kAllowButWarn;
+  } else {
+    return kDeny;
   }
 }
 
@@ -107,12 +146,6 @@
                                       AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(member != nullptr);
-  Runtime* runtime = Runtime::Current();
-
-  if (!runtime->AreHiddenApiChecksEnabled()) {
-    // Exit early. Nothing to enforce.
-    return false;
-  }
 
   Action action = GetMemberAction(member->GetAccessFlags());
   if (action == kAllow) {
@@ -133,14 +166,16 @@
   // We do this regardless of whether we block the access or not.
   WarnAboutMemberAccess(member, access_method);
 
-  // Block access if on blacklist.
   if (action == kDeny) {
+    // Block access
     return true;
   }
 
   // Allow access to this member but print a warning.
   DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
 
+  Runtime* runtime = Runtime::Current();
+
   // 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()) {
@@ -150,7 +185,7 @@
 
   // If this action requires a UI warning, set the appropriate flag.
   if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
-    Runtime::Current()->SetPendingHiddenApiWarning(true);
+    runtime->SetPendingHiddenApiWarning(true);
   }
 
   return false;
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 8913569..cbc2aeb 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -162,19 +162,24 @@
 
 // Must match values in com.android.internal.os.Zygote.
 enum {
-  DEBUG_ENABLE_JDWP               = 1,
-  DEBUG_ENABLE_CHECKJNI           = 1 << 1,
-  DEBUG_ENABLE_ASSERT             = 1 << 2,
-  DEBUG_ENABLE_SAFEMODE           = 1 << 3,
-  DEBUG_ENABLE_JNI_LOGGING        = 1 << 4,
-  DEBUG_GENERATE_DEBUG_INFO       = 1 << 5,
-  DEBUG_ALWAYS_JIT                = 1 << 6,
-  DEBUG_NATIVE_DEBUGGABLE         = 1 << 7,
-  DEBUG_JAVA_DEBUGGABLE           = 1 << 8,
-  DISABLE_VERIFIER                = 1 << 9,
-  ONLY_USE_SYSTEM_OAT_FILES       = 1 << 10,
-  ENABLE_HIDDEN_API_CHECKS        = 1 << 11,
-  DEBUG_GENERATE_MINI_DEBUG_INFO  = 1 << 12,
+  DEBUG_ENABLE_JDWP                  = 1,
+  DEBUG_ENABLE_CHECKJNI              = 1 << 1,
+  DEBUG_ENABLE_ASSERT                = 1 << 2,
+  DEBUG_ENABLE_SAFEMODE              = 1 << 3,
+  DEBUG_ENABLE_JNI_LOGGING           = 1 << 4,
+  DEBUG_GENERATE_DEBUG_INFO          = 1 << 5,
+  DEBUG_ALWAYS_JIT                   = 1 << 6,
+  DEBUG_NATIVE_DEBUGGABLE            = 1 << 7,
+  DEBUG_JAVA_DEBUGGABLE              = 1 << 8,
+  DISABLE_VERIFIER                   = 1 << 9,
+  ONLY_USE_SYSTEM_OAT_FILES          = 1 << 10,
+  DEBUG_GENERATE_MINI_DEBUG_INFO     = 1 << 11,
+  HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12)
+                                     | (1 << 13),
+
+  // bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value
+  // corresponding to hiddenapi::EnforcementPolicy
+  API_ENFORCEMENT_POLICY_SHIFT = CTZ(HIDDEN_API_ENFORCEMENT_POLICY_MASK),
 };
 
 static uint32_t EnableDebugFeatures(uint32_t runtime_flags) {
@@ -285,7 +290,8 @@
   // Our system thread ID, etc, has changed so reset Thread state.
   thread->InitAfterFork();
   runtime_flags = EnableDebugFeatures(runtime_flags);
-  bool do_hidden_api_checks = false;
+  hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kNoChecks;
+  bool dedupe_hidden_api_warnings = true;
 
   if ((runtime_flags & DISABLE_VERIFIER) != 0) {
     Runtime::Current()->DisableVerifier();
@@ -297,10 +303,9 @@
     runtime_flags &= ~ONLY_USE_SYSTEM_OAT_FILES;
   }
 
-  if ((runtime_flags & ENABLE_HIDDEN_API_CHECKS) != 0) {
-    do_hidden_api_checks = true;
-    runtime_flags &= ~ENABLE_HIDDEN_API_CHECKS;
-  }
+  api_enforcement_policy = hiddenapi::EnforcementPolicyFromInt(
+      (runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT);
+  runtime_flags &= ~HIDDEN_API_ENFORCEMENT_POLICY_MASK;
 
   if (runtime_flags != 0) {
     LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags);
@@ -351,11 +356,13 @@
     }
   }
 
+  bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks;
   DCHECK(!(is_system_server && do_hidden_api_checks))
-      << "SystemServer should be forked with ENABLE_HIDDEN_API_CHECKS";
+      << "SystemServer should be forked with EnforcementPolicy::kDisable";
   DCHECK(!(is_zygote && do_hidden_api_checks))
-      << "Child zygote processes should be forked with ENABLE_HIDDEN_API_CHECKS";
-  Runtime::Current()->SetHiddenApiChecksEnabled(do_hidden_api_checks);
+      << "Child zygote processes should be forked with EnforcementPolicy::kDisable";
+  Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy);
+  Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings);
 
   // 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 25d5037..fc61c95 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -89,8 +89,8 @@
 // access hidden APIs. This can be *very* expensive. Never call this in a loop.
 ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  return Runtime::Current()->AreHiddenApiChecksEnabled() &&
-         !IsCallerInBootClassPath(self);
+  hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
+  return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInBootClassPath(self);
 }
 
 // Returns true if the first non-ClassClass caller up the stack should not be
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 7d9d342..53982ae 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -267,7 +267,7 @@
       oat_file_manager_(nullptr),
       is_low_memory_mode_(false),
       safe_mode_(false),
-      do_hidden_api_checks_(false),
+      hidden_api_policy_(hiddenapi::EnforcementPolicy::kNoChecks),
       pending_hidden_api_warning_(false),
       dedupe_hidden_api_warnings_(true),
       always_set_hidden_api_warning_flag_(false),
@@ -1196,9 +1196,14 @@
   // by default and we only enable them if:
   // (a) runtime was started with a flag that enables the checks, or
   // (b) Zygote forked a new process that is not exempt (see ZygoteHooks).
-  do_hidden_api_checks_ = runtime_options.Exists(Opt::HiddenApiChecks);
-  DCHECK(!is_zygote_ || !do_hidden_api_checks_)
-      << "Zygote should not be started with hidden API checks";
+  bool do_hidden_api_checks = runtime_options.Exists(Opt::HiddenApiChecks);
+  DCHECK(!is_zygote_ || !do_hidden_api_checks);
+  // TODO pass the actual enforcement policy in, rather than just a single bit.
+  // As is, we're encoding some logic here about which specific policy to use, which would be better
+  // controlled by the framework.
+  hidden_api_policy_ = do_hidden_api_checks
+      ? hiddenapi::EnforcementPolicy::kBlacklistOnly
+      : hiddenapi::EnforcementPolicy::kNoChecks;
 
   no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain);
   force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index c7f650e..dba31b2 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -49,6 +49,10 @@
 class Heap;
 }  // namespace gc
 
+namespace hiddenapi {
+enum class EnforcementPolicy;
+}  // namespace hiddenapi
+
 namespace jit {
 class Jit;
 class JitOptions;
@@ -520,12 +524,12 @@
   bool IsVerificationEnabled() const;
   bool IsVerificationSoftFail() const;
 
-  void SetHiddenApiChecksEnabled(bool value) {
-    do_hidden_api_checks_ = value;
+  void SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) {
+    hidden_api_policy_ = policy;
   }
 
-  bool AreHiddenApiChecksEnabled() const {
-    return do_hidden_api_checks_;
+  hiddenapi::EnforcementPolicy GetHiddenApiEnforcementPolicy() const {
+    return hidden_api_policy_;
   }
 
   void SetPendingHiddenApiWarning(bool value) {
@@ -990,7 +994,7 @@
   bool safe_mode_;
 
   // Whether access checks on hidden API should be performed.
-  bool do_hidden_api_checks_;
+  hiddenapi::EnforcementPolicy hidden_api_policy_;
 
   // Whether the application has used an API which is not restricted but we
   // should issue a warning about it.
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 67ea64be..bf36ccf 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -24,6 +24,7 @@
 #include <android-base/stringprintf.h>
 
 #include "entrypoints/quick/quick_entrypoints_enum.h"
+#include "hidden_api.h"
 #include "jni_internal.h"
 #include "mirror/class.h"
 #include "mirror/throwable.h"
@@ -287,17 +288,17 @@
  public:
   explicit ScopedHiddenApiExemption(Runtime* runtime)
       : runtime_(runtime),
-        initially_enabled_(runtime_->AreHiddenApiChecksEnabled()) {
-    runtime_->SetHiddenApiChecksEnabled(false);
+        initial_policy_(runtime_->GetHiddenApiEnforcementPolicy()) {
+    runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks);
   }
 
   ~ScopedHiddenApiExemption() {
-    runtime_->SetHiddenApiChecksEnabled(initially_enabled_);
+    runtime_->SetHiddenApiEnforcementPolicy(initial_policy_);
   }
 
  private:
   Runtime* runtime_;
-  const bool initially_enabled_;
+  const hiddenapi::EnforcementPolicy initial_policy_;
   DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiExemption);
 };
 
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index effa37a..04c3fbf 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -16,6 +16,7 @@
 
 #include "class_linker.h"
 #include "dex/art_dex_file_loader.h"
+#include "hidden_api.h"
 #include "jni.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
@@ -27,7 +28,7 @@
 
 extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
   Runtime* runtime = Runtime::Current();
-  runtime->SetHiddenApiChecksEnabled(true);
+  runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly);
   runtime->SetDedupeHiddenApiWarnings(false);
   runtime->AlwaysSetHiddenApiWarningFlag();
 }