Only set UI warning on hidden API dark greylist

The framework might show a toast notification on access to greylisted
hidden APIs. Only show this warning on dark greylist so as to not spam
early testers.

Bug: 64382372
Test: make
Change-Id: I8b5f7b4938e0f238c513e37d7db06856b966802f
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index f476028..05e68e6 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -27,6 +27,7 @@
 enum Action {
   kAllow,
   kAllowButWarn,
+  kAllowButWarnAndToast,
   kDeny
 };
 
@@ -35,8 +36,9 @@
     case HiddenApiAccessFlags::kWhitelist:
       return kAllow;
     case HiddenApiAccessFlags::kLightGreylist:
-    case HiddenApiAccessFlags::kDarkGreylist:
       return kAllowButWarn;
+    case HiddenApiAccessFlags::kDarkGreylist:
+      return kAllowButWarnAndToast;
     case HiddenApiAccessFlags::kBlacklist:
       return kDeny;
   }
@@ -70,8 +72,9 @@
                                       std::function<bool(Thread*)> fn_caller_in_boot)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(member != nullptr);
+  Runtime* runtime = Runtime::Current();
 
-  if (!Runtime::Current()->AreHiddenApiChecksEnabled()) {
+  if (!runtime->AreHiddenApiChecksEnabled()) {
     // Exit early. Nothing to enforce.
     return false;
   }
@@ -90,20 +93,23 @@
   }
 
   // Member is hidden and we are not in the boot class path. Act accordingly.
-  if (action == kAllowButWarn) {
+  if (action == kDeny) {
+    return true;
+  } else {
+    DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
+
     // Allow access to this member but print a warning. Depending on a runtime
     // flag, we might move the member into whitelist and skip the warning the
     // next time the member is used.
-    Runtime::Current()->SetPendingHiddenApiWarning(true);
-    if (Runtime::Current()->ShouldDedupeHiddenApiWarnings()) {
+    if (runtime->ShouldDedupeHiddenApiWarnings()) {
       member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
           member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
     }
     WarnAboutMemberAccess(member);
+    if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
+      Runtime::Current()->SetPendingHiddenApiWarning(true);
+    }
     return false;
-  } else {
-    DCHECK_EQ(action, hiddenapi::kDeny);
-    return true;
   }
 }
 
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index e58fd9d..648a464 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -350,6 +350,9 @@
       << "SystemServer should be forked with DISABLE_HIDDEN_API_CHECKS";
   Runtime::Current()->SetHiddenApiChecksEnabled(do_hidden_api_checks);
 
+  // Clear the hidden API warning flag, in case it was set.
+  Runtime::Current()->SetPendingHiddenApiWarning(false);
+
   if (instruction_set != nullptr && !is_system_server) {
     ScopedUtfChars isa_string(env, instruction_set);
     InstructionSet isa = GetInstructionSetFromString(isa_string.c_str());
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 5a3a6f0..25d83df 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -268,6 +268,7 @@
       do_hidden_api_checks_(true),
       pending_hidden_api_warning_(false),
       dedupe_hidden_api_warnings_(true),
+      always_set_hidden_api_warning_flag_(false),
       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 184e4e5..7ab9be5 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -544,6 +544,14 @@
     return dedupe_hidden_api_warnings_;
   }
 
+  void AlwaysSetHiddenApiWarningFlag() {
+    always_set_hidden_api_warning_flag_ = true;
+  }
+
+  bool ShouldAlwaysSetHiddenApiWarningFlag() const {
+    return always_set_hidden_api_warning_flag_;
+  }
+
   bool IsDexFileFallbackEnabled() const {
     return allow_dex_file_fallback_;
   }
@@ -992,6 +1000,11 @@
   // This is only used for testing.
   bool dedupe_hidden_api_warnings_;
 
+  // Hidden API can print warnings into the log and/or set a flag read by the
+  // framework to show a UI warning. If this flag is set, always set the flag
+  // when there is a warning. This is only used for testing.
+  bool always_set_hidden_api_warning_flag_;
+
   // Whether threads should dump their native stack on SIGQUIT.
   bool dump_native_stack_on_sig_quit_;
 
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index baff6f7..effa37a 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -26,8 +26,10 @@
 namespace Test674HiddenApi {
 
 extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
-  Runtime::Current()->SetHiddenApiChecksEnabled(true);
-  Runtime::Current()->SetDedupeHiddenApiWarnings(false);
+  Runtime* runtime = Runtime::Current();
+  runtime->SetHiddenApiChecksEnabled(true);
+  runtime->SetDedupeHiddenApiWarnings(false);
+  runtime->AlwaysSetHiddenApiWarningFlag();
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_appendToBootClassLoader(