Change runtime behavior related to hiddenapi.

When resolving a method, if the found method is not accessible, check if
there are methods defined in interfaces which are.

Bug: 178680596
Bug: 122551864
Test: 817-hiddenapi
Change-Id: If7631b30b2eb8eb70d3d0fa1522744fbbd1f66a1
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 290d5c1..d4f2e30 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8963,6 +8963,38 @@
   return resolved;
 }
 
+// Return the first accessible method from the list of interfaces implemented by
+// `klass`. For knowing if a method is accessible, we call through
+// `hiddenapi::ShouldDenyAccessToMember`.
+static ArtMethod* FindAccessibleInterfaceMethod(ObjPtr<mirror::Class> klass,
+                                                ObjPtr<mirror::DexCache> dex_cache,
+                                                ObjPtr<mirror::ClassLoader> class_loader,
+                                                ArtMethod* resolved_method,
+                                                PointerSize pointer_size)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+  ObjPtr<mirror::IfTable> iftable = klass->GetIfTable();
+  for (int32_t i = 0, iftable_count = iftable->Count(); i < iftable_count; ++i) {
+    ObjPtr<mirror::PointerArray> methods = iftable->GetMethodArrayOrNull(i);
+    if (methods == nullptr) {
+      continue;
+    }
+    for (size_t j = 0, count = iftable->GetMethodArrayCount(i); j < count; ++j) {
+      if (resolved_method == methods->GetElementPtrSize<ArtMethod*>(j, pointer_size)) {
+        ObjPtr<mirror::Class> iface = iftable->GetInterface(i);
+        ArtMethod* interface_method = &iface->GetVirtualMethodsSlice(pointer_size)[j];
+        // Pass AccessMethod::kNone instead of kLinking to not warn on the
+        // access. We'll only warn later if we could not find a visible method.
+        if (!hiddenapi::ShouldDenyAccessToMember(interface_method,
+                                                 hiddenapi::AccessContext(class_loader, dex_cache),
+                                                 hiddenapi::AccessMethod::kNone)) {
+          return interface_method;
+        }
+      }
+    }
+  }
+  return nullptr;
+}
+
 ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass,
                                            ObjPtr<mirror::DexCache> dex_cache,
                                            ObjPtr<mirror::ClassLoader> class_loader,
@@ -8978,10 +9010,32 @@
   }
   DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
   if (resolved != nullptr &&
+      // We pass AccessMethod::kNone instead of kLinking to not warn yet on the
+      // access, as we'll be looking if the method can be accessed through an
+      // interface.
       hiddenapi::ShouldDenyAccessToMember(resolved,
                                           hiddenapi::AccessContext(class_loader, dex_cache),
-                                          hiddenapi::AccessMethod::kLinking)) {
-    resolved = nullptr;
+                                          hiddenapi::AccessMethod::kNone)) {
+    // The resolved method that we have found cannot be accessed due to
+    // hiddenapi (typically it is declared up the hierarchy and is not an SDK
+    // method). Try to find an interface method from the implemented interfaces which is
+    // accessible.
+    ArtMethod* itf_method = FindAccessibleInterfaceMethod(klass,
+                                                          dex_cache,
+                                                          class_loader,
+                                                          resolved,
+                                                          image_pointer_size_);
+    if (itf_method == nullptr) {
+      // No interface method. Call ShouldDenyAccessToMember again but this time
+      // with AccessMethod::kLinking to ensure that an appropriate warning is
+      // logged.
+      hiddenapi::ShouldDenyAccessToMember(resolved,
+                                          hiddenapi::AccessContext(class_loader, dex_cache),
+                                          hiddenapi::AccessMethod::kLinking);
+      resolved = nullptr;
+    } else {
+      // We found an interface method that is accessible, continue with the resolved method.
+    }
   }
   if (resolved != nullptr) {
     // In case of jmvti, the dex file gets verified before being registered, so first