Print more information about hidden API accesses

This patch changes when we print hidden API access warnings and the
message that is printed:

(a) prints message even if reflection is denied
(b) prints if the access was due to reflection or JNI

Bug: 64382372
Test: manual
Change-Id: I1e59982516c12580a207fd294aa8aab851b1ad46
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 05e68e6..d7e5e18 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -31,6 +31,23 @@
   kDeny
 };
 
+enum AccessMethod {
+  kReflection,
+  kJNI
+};
+
+inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
+  switch (value) {
+    case kReflection:
+      os << "reflection";
+      break;
+    case kJNI:
+      os << "JNI";
+      break;
+  }
+  return os;
+}
+
 inline Action GetMemberAction(uint32_t access_flags) {
   switch (HiddenApiAccessFlags::DecodeFromRuntime(access_flags)) {
     case HiddenApiAccessFlags::kWhitelist:
@@ -45,19 +62,25 @@
 }
 
 // Issue a warning about field access.
-inline void WarnAboutMemberAccess(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) {
+inline void WarnAboutMemberAccess(ArtField* field, AccessMethod access_method)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
   std::string tmp;
   LOG(WARNING) << "Accessing hidden field "
                << field->GetDeclaringClass()->GetDescriptor(&tmp) << "->"
-               << field->GetName() << ":" << field->GetTypeDescriptor();
+               << field->GetName() << ":" << field->GetTypeDescriptor()
+               << " (" << HiddenApiAccessFlags::DecodeFromRuntime(field->GetAccessFlags())
+               << ", " << access_method << ")";
 }
 
 // Issue a warning about method access.
-inline void WarnAboutMemberAccess(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+inline void WarnAboutMemberAccess(ArtMethod* method, AccessMethod access_method)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
   std::string tmp;
   LOG(WARNING) << "Accessing hidden method "
                << method->GetDeclaringClass()->GetDescriptor(&tmp) << "->"
-               << method->GetName() << method->GetSignature().ToString();
+               << method->GetName() << method->GetSignature().ToString()
+               << " (" << HiddenApiAccessFlags::DecodeFromRuntime(method->GetAccessFlags())
+               << ", " << access_method << ")";
 }
 
 // Returns true if access to `member` should be denied to the caller of the
@@ -69,7 +92,8 @@
 template<typename T>
 inline bool ShouldBlockAccessToMember(T* member,
                                       Thread* self,
-                                      std::function<bool(Thread*)> fn_caller_in_boot)
+                                      std::function<bool(Thread*)> fn_caller_in_boot,
+                                      AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(member != nullptr);
   Runtime* runtime = Runtime::Current();
@@ -92,25 +116,33 @@
     return false;
   }
 
-  // Member is hidden and we are not in the boot class path. Act accordingly.
+  // Member is hidden and we are not in the boot class path.
+
+  // Print a log message with information about this class member access.
+  // We do this regardless of whether we block the access or not.
+  WarnAboutMemberAccess(member, access_method);
+
+  // Block access if on blacklist.
   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.
-    if (runtime->ShouldDedupeHiddenApiWarnings()) {
-      member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
-          member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
-    }
-    WarnAboutMemberAccess(member);
-    if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
-      Runtime::Current()->SetPendingHiddenApiWarning(true);
-    }
-    return false;
   }
+
+  // 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 this action requires a UI warning, set the appropriate flag.
+  if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
+    Runtime::Current()->SetPendingHiddenApiWarning(true);
+  }
+
+  return false;
 }
 
 // Returns true if access to member with `access_flags` should be denied to `caller`.
diff --git a/runtime/hidden_api_access_flags.h b/runtime/hidden_api_access_flags.h
index c328f96..6a88c12 100644
--- a/runtime/hidden_api_access_flags.h
+++ b/runtime/hidden_api_access_flags.h
@@ -146,6 +146,24 @@
   };
 };
 
+inline std::ostream& operator<<(std::ostream& os, HiddenApiAccessFlags::ApiList value) {
+  switch (value) {
+    case HiddenApiAccessFlags::kWhitelist:
+      os << "whitelist";
+      break;
+    case HiddenApiAccessFlags::kLightGreylist:
+      os << "light greylist";
+      break;
+    case HiddenApiAccessFlags::kDarkGreylist:
+      os << "dark greylist";
+      break;
+    case HiddenApiAccessFlags::kBlacklist:
+      os << "blacklist";
+      break;
+  }
+  return os;
+}
+
 }  // namespace art
 
 
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 666fb98..cd4d954 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -90,7 +90,8 @@
 template<typename T>
 ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  return hiddenapi::ShouldBlockAccessToMember(member, self, IsCallerInBootClassPath);
+  return hiddenapi::ShouldBlockAccessToMember(
+      member, self, IsCallerInBootClassPath, hiddenapi::kJNI);
 }
 
 // Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 2091a27..4597f68 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -97,7 +97,8 @@
 template<typename T>
 ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  return hiddenapi::ShouldBlockAccessToMember(member, self, IsCallerInBootClassPath);
+  return hiddenapi::ShouldBlockAccessToMember(
+      member, self, IsCallerInBootClassPath, hiddenapi::kReflection);
 }
 
 // Returns true if a class member should be discoverable with reflection given