diff options
-rw-r--r-- | runtime/class_linker.cc | 16 | ||||
-rw-r--r-- | runtime/hidden_api.cc | 56 | ||||
-rw-r--r-- | runtime/hidden_api.h | 46 | ||||
-rw-r--r-- | runtime/interpreter/unstarted_runtime.cc | 6 | ||||
-rw-r--r-- | runtime/jni_internal.cc | 7 | ||||
-rw-r--r-- | runtime/native/java_lang_Class.cc | 35 | ||||
-rw-r--r-- | runtime/well_known_classes.cc | 6 | ||||
-rw-r--r-- | runtime/well_known_classes.h | 3 | ||||
-rw-r--r-- | test/674-hiddenapi/build | 11 | ||||
-rw-r--r-- | test/674-hiddenapi/src-ex/ChildClass.java | 180 |
10 files changed, 235 insertions, 131 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 5e69efea77..3f33f7976e 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7860,8 +7860,8 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass, } DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader, dex_cache, hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) { resolved = nullptr; } if (resolved != nullptr) { @@ -8003,8 +8003,8 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_); } if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { resolved = nullptr; } return resolved; @@ -8083,8 +8083,8 @@ ArtField* ClassLinker::ResolveField(uint32_t field_idx, } if (resolved == nullptr || - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { const char* name = dex_file.GetFieldName(field_id); const char* type = dex_file.GetFieldTypeDescriptor(field_id); ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name); @@ -8117,8 +8117,8 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, StringPiece type(dex_file.GetFieldTypeDescriptor(field_id)); resolved = mirror::Class::FindField(self, klass, name, type); if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { resolved = nullptr; } if (resolved != nullptr) { diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index f0b36a090a..0e72f275d2 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -16,7 +16,11 @@ #include "hidden_api.h" +#include <nativehelper/scoped_local_ref.h> + #include "base/dumpable.h" +#include "thread-current-inl.h" +#include "well_known_classes.h" namespace art { namespace hiddenapi { @@ -111,7 +115,7 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, } template<typename T> -bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access_method) { +Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) { // Get the signature, we need it later. MemberSignature member_signature(member); @@ -138,7 +142,7 @@ bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access if (action == kDeny) { // Block access - return true; + return action; } // Allow access to this member but print a warning. @@ -156,17 +160,49 @@ bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access runtime->SetPendingHiddenApiWarning(true); } - return false; + return action; } // Need to instantiate this. -template bool ShouldBlockAccessToMemberImpl<ArtField>(ArtField* member, - Action action, - AccessMethod access_method); -template bool ShouldBlockAccessToMemberImpl<ArtMethod>(ArtMethod* member, - Action action, - AccessMethod access_method); - +template Action GetMemberActionImpl<ArtField>(ArtField* member, + Action action, + AccessMethod access_method); +template Action GetMemberActionImpl<ArtMethod>(ArtMethod* member, + Action action, + AccessMethod access_method); } // namespace detail + +template<typename T> +void NotifyHiddenApiListener(T* member) { + Runtime* runtime = Runtime::Current(); + if (!runtime->IsAotCompiler()) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + + ScopedLocalRef<jobject> consumer_object(soa.Env(), + soa.Env()->GetStaticObjectField( + WellKnownClasses::dalvik_system_VMRuntime, + WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer)); + // If the consumer is non-null, we call back to it to let it know that we + // have encountered an API that's in one of our lists. + if (consumer_object != nullptr) { + detail::MemberSignature member_signature(member); + std::ostringstream member_signature_str; + member_signature.Dump(member_signature_str); + + ScopedLocalRef<jobject> signature_str( + soa.Env(), + soa.Env()->NewStringUTF(member_signature_str.str().c_str())); + + // Call through to Consumer.accept(String memberSignature); + soa.Env()->CallVoidMethod(consumer_object.get(), + WellKnownClasses::java_util_function_Consumer_accept, + signature_str.get()); + } + } +} + +template void NotifyHiddenApiListener<ArtMethod>(ArtMethod* member); +template void NotifyHiddenApiListener<ArtField>(ArtField* member); + } // namespace hiddenapi } // namespace art diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index cc6c146f00..ffdeacbfff 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -58,7 +58,7 @@ enum AccessMethod { kLinking, }; -inline Action GetMemberAction(uint32_t access_flags) { +inline Action GetActionFromAccessFlags(uint32_t access_flags) { EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); if (policy == EnforcementPolicy::kNoChecks) { // Exit early. Nothing to enforce. @@ -108,9 +108,7 @@ class MemberSignature { }; template<typename T> -bool ShouldBlockAccessToMemberImpl(T* member, - Action action, - AccessMethod access_method) +Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if the caller is either loaded by the boot strap class loader or comes from @@ -138,28 +136,28 @@ inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loade // return true if the caller is located in the platform. // This function might print warnings into the log if the member is hidden. template<typename T> -inline bool ShouldBlockAccessToMember(T* member, - Thread* self, - std::function<bool(Thread*)> fn_caller_in_platform, - AccessMethod access_method) +inline Action GetMemberAction(T* member, + Thread* self, + std::function<bool(Thread*)> fn_caller_in_platform, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); - Action action = GetMemberAction(member->GetAccessFlags()); + Action action = GetActionFromAccessFlags(member->GetAccessFlags()); if (action == kAllow) { // Nothing to do. - return false; + return action; } // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access. // This can be *very* expensive. Save it for last. if (fn_caller_in_platform(self)) { // Caller in the platform. Exit. - return false; + return kAllow; } // Member is hidden and caller is not in the platform. - return detail::ShouldBlockAccessToMemberImpl(member, action, access_method); + return detail::GetMemberActionImpl(member, action, access_method); } inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller) @@ -172,18 +170,26 @@ inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller) // `caller_class_loader`. // This function might print warnings into the log if the member is hidden. template<typename T> -inline bool ShouldBlockAccessToMember(T* member, - ObjPtr<mirror::ClassLoader> caller_class_loader, - ObjPtr<mirror::DexCache> caller_dex_cache, - AccessMethod access_method) +inline Action GetMemberAction(T* member, + ObjPtr<mirror::ClassLoader> caller_class_loader, + ObjPtr<mirror::DexCache> caller_dex_cache, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache); - return ShouldBlockAccessToMember(member, - /* thread */ nullptr, - [caller_in_platform] (Thread*) { return caller_in_platform; }, - access_method); + return GetMemberAction(member, + /* thread */ nullptr, + [caller_in_platform] (Thread*) { return caller_in_platform; }, + access_method); } +// Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that +// |member| was accessed. This is usually called when an API is on the black, +// dark grey or light grey lists. Given that the callback can execute arbitrary +// code, a call to this method can result in thread suspension. +template<typename T> void NotifyHiddenApiListener(T* member) + REQUIRES_SHARED(Locks::mutator_lock_); + + } // namespace hiddenapi } // namespace art diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 4c7a97dfa8..dd8d7dd3e2 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -181,11 +181,13 @@ static mirror::String* GetClassName(Thread* self, ShadowFrame* shadow_frame, siz template<typename T> static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* frame) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember( + // All uses in this file are from reflection + constexpr hiddenapi::AccessMethod access_method = hiddenapi::kReflection; + return hiddenapi::GetMemberAction( member, frame->GetMethod()->GetDeclaringClass()->GetClassLoader(), frame->GetMethod()->GetDeclaringClass()->GetDexCache(), - hiddenapi::kReflection); // all uses in this file are from reflection + access_method) == hiddenapi::kDeny; } void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self, diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index f309581735..9dbcded867 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -87,8 +87,13 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l template<typename T> ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember( + hiddenapi::Action action = hiddenapi::GetMemberAction( member, self, IsCallerInPlatformDex, hiddenapi::kJNI); + if (action != hiddenapi::kAllow) { + hiddenapi::NotifyHiddenApiListener(member); + } + + return action == hiddenapi::kDeny; } // 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 ad05856eaf..a8b203bff2 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -98,8 +98,13 @@ ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) template<typename T> ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember( + hiddenapi::Action action = hiddenapi::GetMemberAction( member, self, IsCallerInPlatformDex, hiddenapi::kReflection); + if (action != hiddenapi::kAllow) { + hiddenapi::NotifyHiddenApiListener(member); + } + + return action == hiddenapi::kDeny; } // Returns true if a class member should be discoverable with reflection given @@ -113,7 +118,8 @@ ALWAYS_INLINE static bool IsDiscoverable(bool public_only, return false; } - if (enforce_hidden_api && hiddenapi::GetMemberAction(access_flags) == hiddenapi::kDeny) { + if (enforce_hidden_api && + hiddenapi::GetActionFromAccessFlags(access_flags) == hiddenapi::kDeny) { return false; } @@ -433,12 +439,14 @@ static jobject Class_getPublicFieldRecursive(JNIEnv* env, jobject javaThis, jstr return nullptr; } - mirror::Field* field = GetPublicFieldRecursive( - soa.Self(), DecodeClass(soa, javaThis), name_string); - if (field == nullptr || ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) { + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::Field> field = hs.NewHandle(GetPublicFieldRecursive( + soa.Self(), DecodeClass(soa, javaThis), name_string)); + if (field.Get() == nullptr || + ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) { return nullptr; } - return soa.AddLocalReference<jobject>(field); + return soa.AddLocalReference<jobject>(field.Get()); } static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring name) { @@ -477,15 +485,17 @@ static jobject Class_getDeclaredConstructorInternal( ScopedFastNativeObjectAccess soa(env); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); - ObjPtr<mirror::Constructor> result = + + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::Constructor> result = hs.NewHandle( mirror::Class::GetDeclaredConstructorInternal<kRuntimePointerSize, false>( soa.Self(), DecodeClass(soa, javaThis), - soa.Decode<mirror::ObjectArray<mirror::Class>>(args)); + soa.Decode<mirror::ObjectArray<mirror::Class>>(args))); if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } - return soa.AddLocalReference<jobject>(result); + return soa.AddLocalReference<jobject>(result.Get()); } static ALWAYS_INLINE inline bool MethodMatchesConstructor( @@ -535,18 +545,19 @@ static jobjectArray Class_getDeclaredConstructorsInternal( static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, jstring name, jobjectArray args) { ScopedFastNativeObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); - ObjPtr<mirror::Method> result = + Handle<mirror::Method> result = hs.NewHandle( mirror::Class::GetDeclaredMethodInternal<kRuntimePointerSize, false>( soa.Self(), DecodeClass(soa, javaThis), soa.Decode<mirror::String>(name), - soa.Decode<mirror::ObjectArray<mirror::Class>>(args)); + soa.Decode<mirror::ObjectArray<mirror::Class>>(args))); if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } - return soa.AddLocalReference<jobject>(result); + return soa.AddLocalReference<jobject>(result.Get()); } static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index bf36ccf0fa..742e713774 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -77,6 +77,7 @@ jclass WellKnownClasses::java_nio_ByteBuffer; jclass WellKnownClasses::java_nio_DirectByteBuffer; jclass WellKnownClasses::java_util_ArrayList; jclass WellKnownClasses::java_util_Collections; +jclass WellKnownClasses::java_util_function_Consumer; jclass WellKnownClasses::libcore_reflect_AnnotationFactory; jclass WellKnownClasses::libcore_reflect_AnnotationMember; jclass WellKnownClasses::libcore_util_EmptyArray; @@ -115,6 +116,7 @@ jmethodID WellKnownClasses::java_lang_Thread_run; jmethodID WellKnownClasses::java_lang_ThreadGroup_add; jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread; jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init; +jmethodID WellKnownClasses::java_util_function_Consumer_accept; jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation; jmethodID WellKnownClasses::libcore_reflect_AnnotationMember_init; jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -125,6 +127,7 @@ jfieldID WellKnownClasses::dalvik_system_DexFile_fileName; jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList; jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements; jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile; +jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer; jfieldID WellKnownClasses::java_lang_Thread_daemon; jfieldID WellKnownClasses::java_lang_Thread_group; jfieldID WellKnownClasses::java_lang_Thread_lock; @@ -349,6 +352,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer"); java_util_ArrayList = CacheClass(env, "java/util/ArrayList"); java_util_Collections = CacheClass(env, "java/util/Collections"); + java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer"); libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory"); libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember"); libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray"); @@ -379,6 +383,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V"); java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V"); java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "<init>", "(JI)V"); + java_util_function_Consumer_accept = CacheMethod(env, java_util_function_Consumer, false, "accept", "(Ljava/lang/Object;)V"); libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;"); libcore_reflect_AnnotationMember_init = CacheMethod(env, libcore_reflect_AnnotationMember, false, "<init>", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V"); org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V"); @@ -389,6 +394,7 @@ void WellKnownClasses::Init(JNIEnv* env) { dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;"); dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;"); dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;"); + dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(env, dalvik_system_VMRuntime, true, "nonSdkApiUsageConsumer", "Ljava/util/function/Consumer;"); java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z"); java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;"); java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index d5d7033132..7b1a2943d2 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -85,6 +85,7 @@ struct WellKnownClasses { static jclass java_lang_Throwable; static jclass java_util_ArrayList; static jclass java_util_Collections; + static jclass java_util_function_Consumer; static jclass java_nio_ByteBuffer; static jclass java_nio_DirectByteBuffer; static jclass libcore_reflect_AnnotationFactory; @@ -125,6 +126,7 @@ struct WellKnownClasses { static jmethodID java_lang_ThreadGroup_add; static jmethodID java_lang_ThreadGroup_removeThread; static jmethodID java_nio_DirectByteBuffer_init; + static jmethodID java_util_function_Consumer_accept; static jmethodID libcore_reflect_AnnotationFactory_createAnnotation; static jmethodID libcore_reflect_AnnotationMember_init; static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -135,6 +137,7 @@ struct WellKnownClasses { static jfieldID dalvik_system_DexFile_fileName; static jfieldID dalvik_system_DexPathList_dexElements; static jfieldID dalvik_system_DexPathList__Element_dexFile; + static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer; static jfieldID java_lang_reflect_Executable_artMethod; static jfieldID java_lang_reflect_Proxy_h; static jfieldID java_lang_Thread_daemon; diff --git a/test/674-hiddenapi/build b/test/674-hiddenapi/build index 9012e8fd13..330a6def29 100644 --- a/test/674-hiddenapi/build +++ b/test/674-hiddenapi/build @@ -16,15 +16,6 @@ set -e -# Special build logic to handle src-ex .java files which have code that only builds on RI. -custom_build_logic() { - [[ -d ignore.src-ex ]] && mv ignore.src-ex src-ex - # src-ex uses code that can only build on RI. - ${JAVAC} -source 1.8 -target 1.8 -sourcepath src-ex -sourcepath src -d classes-ex $(find src-ex -name '*.java') - # remove src-ex so that default-build doesn't try to build it. - [[ -d src-ex ]] && mv src-ex ignore.src-ex -} - # Build the jars twice. First with applying hiddenapi, creating a boot jar, then # a second time without to create a normal jar. We need to do this because we # want to load the jar once as an app module and once as a member of the boot @@ -33,7 +24,6 @@ custom_build_logic() { # class path dex files, so the boot jar loads fine in the latter case. export USE_HIDDENAPI=true -custom_build_logic ./default-build "$@" # Move the jar file into the resource folder to be bundled with the test. @@ -45,5 +35,4 @@ mv ${TEST_NAME}.jar res/boot.jar rm -rf classes* export USE_HIDDENAPI=false -custom_build_logic ./default-build "$@" diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index 582e907ca3..822224c539 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import dalvik.system.VMRuntime; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -21,11 +22,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; - -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.type.PrimitiveType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeVisitor; +import java.util.function.Consumer; public class ChildClass { enum PrimitiveType { @@ -136,6 +133,47 @@ public class ChildClass { } } + static final class RecordingConsumer implements Consumer<String> { + public String recordedValue = null; + + @Override + public void accept(String value) { + recordedValue = value; + } + } + + private static void checkMemberCallback(Class<?> klass, String name, + boolean isPublic, boolean isField) { + try { + RecordingConsumer consumer = new RecordingConsumer(); + VMRuntime.setNonSdkApiUsageConsumer(consumer); + try { + if (isPublic) { + if (isField) { + klass.getField(name); + } else { + klass.getMethod(name); + } + } else { + if (isField) { + klass.getDeclaredField(name); + } else { + klass.getDeclaredMethod(name); + } + } + } catch (NoSuchFieldException|NoSuchMethodException ignored) { + // We're not concerned whether an exception is thrown or not - we're + // only interested in whether the callback is invoked. + } + + if (consumer.recordedValue == null || !consumer.recordedValue.contains(name)) { + throw new RuntimeException("No callback for member: " + name); + } + } finally { + VMRuntime.setNonSdkApiUsageConsumer(null); + } + } + private static void checkField(Class<?> klass, String name, boolean isStatic, Visibility visibility, Behaviour behaviour) throws Exception { @@ -174,48 +212,52 @@ public class ChildClass { // Finish here if we could not discover the field. - if (!canDiscover) { - return; - } + if (canDiscover) { + // Test that modifiers are unaffected. - // Test that modifiers are unaffected. + if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) { + throwModifiersException(klass, name, true); + } - if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) { - throwModifiersException(klass, name, true); - } + // Test getters and setters when meaningful. - // Test getters and setters when meaningful. + clearWarning(); + if (!Reflection.canGetField(klass, name)) { + throwAccessException(klass, name, true, "Field.getInt()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "Field.getInt()", setsWarning); + } - clearWarning(); - if (!Reflection.canGetField(klass, name)) { - throwAccessException(klass, name, true, "Field.getInt()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.getInt()", setsWarning); - } + clearWarning(); + if (!Reflection.canSetField(klass, name)) { + throwAccessException(klass, name, true, "Field.setInt()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "Field.setInt()", setsWarning); + } - clearWarning(); - if (!Reflection.canSetField(klass, name)) { - throwAccessException(klass, name, true, "Field.setInt()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.setInt()", setsWarning); - } + clearWarning(); + if (!JNI.canGetField(klass, name, isStatic)) { + throwAccessException(klass, name, true, "getIntField"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "getIntField", setsWarning); + } - clearWarning(); - if (!JNI.canGetField(klass, name, isStatic)) { - throwAccessException(klass, name, true, "getIntField"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "getIntField", setsWarning); + clearWarning(); + if (!JNI.canSetField(klass, name, isStatic)) { + throwAccessException(klass, name, true, "setIntField"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "setIntField", setsWarning); + } } + // Test that callbacks are invoked correctly. clearWarning(); - if (!JNI.canSetField(klass, name, isStatic)) { - throwAccessException(klass, name, true, "setIntField"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "setIntField", setsWarning); + if (setsWarning || !canDiscover) { + checkMemberCallback(klass, name, isPublic, true /* isField */); } } @@ -257,42 +299,46 @@ public class ChildClass { // Finish here if we could not discover the field. - if (!canDiscover) { - return; - } + if (canDiscover) { + // Test that modifiers are unaffected. - // Test that modifiers are unaffected. + if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) { + throwModifiersException(klass, name, false); + } - if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) { - throwModifiersException(klass, name, false); - } + // Test whether we can invoke the method. This skips non-static interface methods. - // Test whether we can invoke the method. This skips non-static interface methods. + if (!klass.isInterface() || isStatic) { + clearWarning(); + if (!Reflection.canInvokeMethod(klass, name)) { + throwAccessException(klass, name, false, "invoke()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "invoke()", setsWarning); + } - if (!klass.isInterface() || isStatic) { - clearWarning(); - if (!Reflection.canInvokeMethod(klass, name)) { - throwAccessException(klass, name, false, "invoke()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "invoke()", setsWarning); - } + clearWarning(); + if (!JNI.canInvokeMethodA(klass, name, isStatic)) { + throwAccessException(klass, name, false, "CallMethodA"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "CallMethodA()", setsWarning); + } - clearWarning(); - if (!JNI.canInvokeMethodA(klass, name, isStatic)) { - throwAccessException(klass, name, false, "CallMethodA"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodA()", setsWarning); + clearWarning(); + if (!JNI.canInvokeMethodV(klass, name, isStatic)) { + throwAccessException(klass, name, false, "CallMethodV"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "CallMethodV()", setsWarning); + } } + } - clearWarning(); - if (!JNI.canInvokeMethodV(klass, name, isStatic)) { - throwAccessException(klass, name, false, "CallMethodV"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodV()", setsWarning); - } + // Test that callbacks are invoked correctly. + clearWarning(); + if (setsWarning || !canDiscover) { + checkMemberCallback(klass, name, isPublic, false /* isField */); } } |