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