summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/class_linker.cc16
-rw-r--r--runtime/hidden_api.cc56
-rw-r--r--runtime/hidden_api.h46
-rw-r--r--runtime/interpreter/unstarted_runtime.cc6
-rw-r--r--runtime/jni_internal.cc7
-rw-r--r--runtime/native/java_lang_Class.cc35
-rw-r--r--runtime/well_known_classes.cc6
-rw-r--r--runtime/well_known_classes.h3
-rw-r--r--test/674-hiddenapi/build11
-rw-r--r--test/674-hiddenapi/src-ex/ChildClass.java180
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 */);
}
}