Agent libraries need to be searched for JNI functions
This makes agent libraries the option of last resort for native
method implementations. This will allow one to not need to manually
link all native methods in an agent library.
Bug: 37522517
Bug: 37432636
Test: ./test.py --host -j40
Change-Id: I5ad78a15e7e2799d2a877c5d603342899e2a1bd1
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index b93b8f2..c8dc2f2 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -40,6 +40,7 @@
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change-inl.h"
#include "sigchain.h"
+#include "ti/agent.h"
#include "thread-inl.h"
#include "thread_list.h"
@@ -268,7 +269,6 @@
detail += "No implementation found for ";
detail += m->PrettyMethod();
detail += " (tried " + jni_short_name + " and " + jni_long_name + ")";
- LOG(ERROR) << detail;
return nullptr;
}
@@ -929,6 +929,26 @@
return was_successful;
}
+static void* FindCodeForNativeMethodInAgents(ArtMethod* m) REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::string jni_short_name(m->JniShortName());
+ std::string jni_long_name(m->JniLongName());
+ for (const ti::Agent& agent : Runtime::Current()->GetAgents()) {
+ void* fn = agent.FindSymbol(jni_short_name);
+ if (fn != nullptr) {
+ VLOG(jni) << "Found implementation for " << m->PrettyMethod()
+ << " (symbol: " << jni_short_name << ") in " << agent;
+ return fn;
+ }
+ fn = agent.FindSymbol(jni_long_name);
+ if (fn != nullptr) {
+ VLOG(jni) << "Found implementation for " << m->PrettyMethod()
+ << " (symbol: " << jni_long_name << ") in " << agent;
+ return fn;
+ }
+ }
+ return nullptr;
+}
+
void* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m) {
CHECK(m->IsNative());
mirror::Class* c = m->GetDeclaringClass();
@@ -941,8 +961,14 @@
MutexLock mu(self, *Locks::jni_libraries_lock_);
native_method = libraries_->FindNativeMethod(m, detail);
}
+ if (native_method == nullptr) {
+ // Lookup JNI native methods from native TI Agent libraries. See runtime/ti/agent.h for more
+ // information. Agent libraries are searched for native methods after all jni libraries.
+ native_method = FindCodeForNativeMethodInAgents(m);
+ }
// Throwing can cause libraries_lock to be reacquired.
if (native_method == nullptr) {
+ LOG(ERROR) << detail;
self->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;", detail.c_str());
}
return native_method;
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index b146b51..2626eef 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2159,10 +2159,11 @@
}
CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR);
ScopedObjectAccess soa(env);
- ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(java_class);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::Class> c = hs.NewHandle(soa.Decode<mirror::Class>(java_class));
if (UNLIKELY(method_count == 0)) {
LOG(WARNING) << "JNI RegisterNativeMethods: attempt to register 0 native methods for "
- << mirror::Class::PrettyDescriptor(c);
+ << c->PrettyDescriptor();
return JNI_OK;
}
CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods, JNI_ERR);
@@ -2171,13 +2172,13 @@
const char* sig = methods[i].signature;
const void* fnPtr = methods[i].fnPtr;
if (UNLIKELY(name == nullptr)) {
- ReportInvalidJNINativeMethod(soa, c, "method name", i, return_errors);
+ ReportInvalidJNINativeMethod(soa, c.Get(), "method name", i, return_errors);
return JNI_ERR;
} else if (UNLIKELY(sig == nullptr)) {
- ReportInvalidJNINativeMethod(soa, c, "method signature", i, return_errors);
+ ReportInvalidJNINativeMethod(soa, c.Get(), "method signature", i, return_errors);
return JNI_ERR;
} else if (UNLIKELY(fnPtr == nullptr)) {
- ReportInvalidJNINativeMethod(soa, c, "native function", i, return_errors);
+ ReportInvalidJNINativeMethod(soa, c.Get(), "native function", i, return_errors);
return JNI_ERR;
}
bool is_fast = false;
@@ -2220,7 +2221,7 @@
// the parent.
ArtMethod* m = nullptr;
bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->vm->IsCheckJniEnabled();
- for (ObjPtr<mirror::Class> current_class = c;
+ for (ObjPtr<mirror::Class> current_class = c.Get();
current_class != nullptr;
current_class = current_class->GetSuperClass()) {
// Search first only comparing methods which are native.
@@ -2252,14 +2253,14 @@
<< "Failed to register native method "
<< c->PrettyDescriptor() << "." << name << sig << " in "
<< c->GetDexCache()->GetLocation()->ToModifiedUtf8();
- ThrowNoSuchMethodError(soa, c, name, sig, "static or non-static");
+ ThrowNoSuchMethodError(soa, c.Get(), name, sig, "static or non-static");
return JNI_ERR;
} else if (!m->IsNative()) {
LOG(return_errors ? ::android::base::ERROR : ::android::base::FATAL)
<< "Failed to register non-native method "
<< c->PrettyDescriptor() << "." << name << sig
<< " as native";
- ThrowNoSuchMethodError(soa, c, name, sig, "native");
+ ThrowNoSuchMethodError(soa, c.Get(), name, sig, "native");
return JNI_ERR;
}
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 20db628..b91cb0c 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -657,6 +657,10 @@
void AttachAgent(const std::string& agent_arg);
+ const std::list<ti::Agent>& GetAgents() const {
+ return agents_;
+ }
+
RuntimeCallbacks* GetRuntimeCallbacks();
void InitThreadGroups(Thread* self);
diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc
index 0bba44c..86f5282 100644
--- a/runtime/ti/agent.cc
+++ b/runtime/ti/agent.cc
@@ -72,6 +72,11 @@
}
}
+void* Agent::FindSymbol(const std::string& name) const {
+ CHECK(IsStarted()) << "Cannot find symbols in an unloaded agent library " << this;
+ return dlsym(dlopen_handle_, name.c_str());
+}
+
Agent::LoadError Agent::DoDlOpen(/*out*/std::string* error_msg) {
DCHECK(error_msg != nullptr);
@@ -86,18 +91,15 @@
return kLoadingError;
}
- onload_ = reinterpret_cast<AgentOnLoadFunction>(dlsym(dlopen_handle_,
- AGENT_ON_LOAD_FUNCTION_NAME));
+ onload_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_LOAD_FUNCTION_NAME));
if (onload_ == nullptr) {
VLOG(agents) << "Unable to find 'Agent_OnLoad' symbol in " << this;
}
- onattach_ = reinterpret_cast<AgentOnLoadFunction>(dlsym(dlopen_handle_,
- AGENT_ON_ATTACH_FUNCTION_NAME));
+ onattach_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_ATTACH_FUNCTION_NAME));
if (onattach_ == nullptr) {
VLOG(agents) << "Unable to find 'Agent_OnAttach' symbol in " << this;
}
- onunload_= reinterpret_cast<AgentOnUnloadFunction>(dlsym(dlopen_handle_,
- AGENT_ON_UNLOAD_FUNCTION_NAME));
+ onunload_= reinterpret_cast<AgentOnUnloadFunction>(FindSymbol(AGENT_ON_UNLOAD_FUNCTION_NAME));
if (onunload_ == nullptr) {
VLOG(agents) << "Unable to find 'Agent_OnUnload' symbol in " << this;
}
diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h
index 7408aee..b5ecba1 100644
--- a/runtime/ti/agent.h
+++ b/runtime/ti/agent.h
@@ -29,8 +29,14 @@
using AgentOnLoadFunction = jint (*)(JavaVM*, const char*, void*);
using AgentOnUnloadFunction = void (*)(JavaVM*);
+// Agents are native libraries that will be loaded by the runtime for the purpose of
+// instrumentation. They will be entered by Agent_OnLoad or Agent_OnAttach depending on whether the
+// agent is being attached during runtime startup or later.
+//
+// The agent's Agent_OnUnload function will be called during runtime shutdown.
+//
// TODO: consider splitting ti::Agent into command line, agent and shared library handler classes
-
+// TODO Support native-bridge. Currently agents can only be the actual runtime ISA of the device.
class Agent {
public:
enum LoadError {
@@ -56,6 +62,8 @@
return !GetArgs().empty();
}
+ void* FindSymbol(const std::string& name) const;
+
LoadError Load(/*out*/jint* call_res, /*out*/std::string* error_msg) {
VLOG(agents) << "Loading agent: " << name_ << " " << args_;
return DoLoadHelper(false, call_res, error_msg);
diff --git a/test/986-native-method-bind/expected.txt b/test/986-native-method-bind/expected.txt
index 189217d..3376e6f 100644
--- a/test/986-native-method-bind/expected.txt
+++ b/test/986-native-method-bind/expected.txt
@@ -1,8 +1,10 @@
-private static native void art.Test986$Transform.sayHi() = Java_art_Test986_00024Transform_sayHi -> Java_art_Test986_00024Transform_sayHi
-Hello
-private static native void art.Test986$Transform.sayHi() = Java_art_Test986_00024Transform_sayHi -> NoReallySayGoodbye
+private static native void art.Test986$Transform.sayHi2() = Java_art_Test986_00024Transform_sayHi2 -> Java_art_Test986_00024Transform_sayHi2
+Hello - 2
+private static native void art.Test986$Transform.sayHi() = Java_art_Test986_00024Transform_sayHi__ -> NoReallySayGoodbye
Bye
-public static native void art.Main.bindAgentJNI(java.lang.String,java.lang.ClassLoader) = Java_art_Main_bindAgentJNI -> Java_art_Main_bindAgentJNI
-public static native void art.Main.bindAgentJNIForClass(java.lang.Class) = Java_art_Main_bindAgentJNIForClass -> Java_art_Main_bindAgentJNIForClass
-private static native void art.Test986.setNativeBindNotify(boolean) = Java_art_Test986_setNativeBindNotify -> Java_art_Test986_setNativeBindNotify
-private static native void art.Test986.setupNativeBindNotify() = Java_art_Test986_setupNativeBindNotify -> Java_art_Test986_setupNativeBindNotify
+private static native void art.Test986$Transform.sayHi() = Java_art_Test986_00024Transform_sayHi__ -> Java_art_Test986_00024Transform_sayHi2
+private static native void art.Test986$Transform.sayHi2() = Java_art_Test986_00024Transform_sayHi2 -> Java_art_Test986_00024Transform_sayHi2
+Hello - 2
+private static native void art.Test986$Transform.sayHi() = Java_art_Test986_00024Transform_sayHi__ -> Java_art_Test986_00024Transform_sayHi__
+private static native void art.Test986$Transform.sayHi2() = Java_art_Test986_00024Transform_sayHi2 -> Java_art_Test986_00024Transform_sayHi2
+Hello
diff --git a/test/986-native-method-bind/native_bind.cc b/test/986-native-method-bind/native_bind.cc
index 4f93f87..eec635b 100644
--- a/test/986-native-method-bind/native_bind.cc
+++ b/test/986-native-method-bind/native_bind.cc
@@ -38,11 +38,16 @@
env->CallStaticVoidMethod(klass.get(), targetMethod);
}
-extern "C" JNIEXPORT void JNICALL Java_art_Test986_00024Transform_sayHi(
+extern "C" JNIEXPORT void JNICALL Java_art_Test986_00024Transform_sayHi__(
JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
doUpPrintCall(env, "doSayHi");
}
+extern "C" JNIEXPORT void JNICALL Java_art_Test986_00024Transform_sayHi2(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ doUpPrintCall(env, "doSayHi2");
+}
+
extern "C" JNIEXPORT void JNICALL NoReallySayGoodbye(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
doUpPrintCall(env, "doSayBye");
}
@@ -106,5 +111,17 @@
}
}
+extern "C" JNIEXPORT void JNICALL Java_art_Test986_rebindTransformClass(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jclass k) {
+ JNINativeMethod m[2];
+ m[0].name= "sayHi";
+ m[0].signature = "()V";
+ m[0].fnPtr = reinterpret_cast<void*>(Java_art_Test986_00024Transform_sayHi__);
+ m[1].name= "sayHi2";
+ m[1].signature = "()V";
+ m[1].fnPtr = reinterpret_cast<void*>(Java_art_Test986_00024Transform_sayHi2);
+ env->RegisterNatives(k, m, 2);
+}
+
} // namespace Test986NativeBind
} // namespace art
diff --git a/test/986-native-method-bind/src/art/Redefinition.java b/test/986-native-method-bind/src/art/Redefinition.java
deleted file mode 100644
index 0350ab4..0000000
--- a/test/986-native-method-bind/src/art/Redefinition.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package art;
-
-import java.util.ArrayList;
-// Common Redefinition functions. Placed here for use by CTS
-public class Redefinition {
- // Bind native functions.
- static {
- Main.bindAgentJNIForClass(Redefinition.class);
- }
-
- public static final class CommonClassDefinition {
- public final Class<?> target;
- public final byte[] class_file_bytes;
- public final byte[] dex_file_bytes;
-
- public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
- this.target = target;
- this.class_file_bytes = class_file_bytes;
- this.dex_file_bytes = dex_file_bytes;
- }
- }
-
- // A set of possible test configurations. Test should set this if they need to.
- // This must be kept in sync with the defines in ti-agent/common_helper.cc
- public static enum Config {
- COMMON_REDEFINE(0),
- COMMON_RETRANSFORM(1),
- COMMON_TRANSFORM(2);
-
- private final int val;
- private Config(int val) {
- this.val = val;
- }
- }
-
- public static void setTestConfiguration(Config type) {
- nativeSetTestConfiguration(type.val);
- }
-
- private static native void nativeSetTestConfiguration(int type);
-
- // Transforms the class
- public static native void doCommonClassRedefinition(Class<?> target,
- byte[] classfile,
- byte[] dexfile);
-
- public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
- ArrayList<Class<?>> classes = new ArrayList<>();
- ArrayList<byte[]> class_files = new ArrayList<>();
- ArrayList<byte[]> dex_files = new ArrayList<>();
-
- for (CommonClassDefinition d : defs) {
- classes.add(d.target);
- class_files.add(d.class_file_bytes);
- dex_files.add(d.dex_file_bytes);
- }
- doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
- class_files.toArray(new byte[0][]),
- dex_files.toArray(new byte[0][]));
- }
-
- public static void addMultiTransformationResults(CommonClassDefinition... defs) {
- for (CommonClassDefinition d : defs) {
- addCommonTransformationResult(d.target.getCanonicalName(),
- d.class_file_bytes,
- d.dex_file_bytes);
- }
- }
-
- public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
- byte[][] classfiles,
- byte[][] dexfiles);
- public static native void doCommonClassRetransformation(Class<?>... target);
- public static native void setPopRetransformations(boolean pop);
- public static native void popTransformationFor(String name);
- public static native void enableCommonRetransformation(boolean enable);
- public static native void addCommonTransformationResult(String target_name,
- byte[] class_bytes,
- byte[] dex_bytes);
-}
diff --git a/test/986-native-method-bind/src/art/Test986.java b/test/986-native-method-bind/src/art/Test986.java
index edd2e9d..aac73d3 100644
--- a/test/986-native-method-bind/src/art/Test986.java
+++ b/test/986-native-method-bind/src/art/Test986.java
@@ -32,6 +32,7 @@
// A class with a native method we can play with.
static class Transform {
private static native void sayHi();
+ private static native void sayHi2();
}
public static void run() throws Exception {
@@ -44,6 +45,10 @@
SymbolMap.put(method, dest);
}
+ private static void removeNativeTransform(Method method) {
+ SymbolMap.remove(method);
+ }
+
/**
* Notifies java that a native method bind has occurred and requests the new symbol to bind to.
*/
@@ -58,14 +63,23 @@
public static void doTest() throws Exception {
Method say_hi_method = Transform.class.getDeclaredMethod("sayHi");
- // TODO We should test auto-binding but due to the way this is run that will be annoying.
- Main.bindAgentJNIForClass(Transform.class);
- Transform.sayHi();
+
+ // Test we will bind fine if we make no changes.
+ Transform.sayHi2();
+
+ // Test we can get in the middle of autobind
setNativeTransform(say_hi_method, "NoReallySayGoodbye");
- Main.bindAgentJNIForClass(Transform.class);
Transform.sayHi();
- Main.bindAgentJNIForClass(Main.class);
- Main.bindAgentJNIForClass(Test986.class);
+
+ // Test we can get in between manual bind.
+ setNativeTransform(say_hi_method, "Java_art_Test986_00024Transform_sayHi2");
+ rebindTransformClass();
+ Transform.sayHi();
+
+ // Test we can get rid of transform
+ removeNativeTransform(say_hi_method);
+ rebindTransformClass();
+ Transform.sayHi();
}
// Functions called from native code.
@@ -73,10 +87,18 @@
System.out.println("Hello");
}
+ public static void doSayHi2() {
+ System.out.println("Hello - 2");
+ }
+
public static void doSayBye() {
System.out.println("Bye");
}
private static native void setNativeBindNotify(boolean enable);
private static native void setupNativeBindNotify();
+ private static void rebindTransformClass() {
+ rebindTransformClass(Transform.class);
+ }
+ private static native void rebindTransformClass(Class<?> trans);
}
diff --git a/test/987-agent-bind/agent_bind.cc b/test/987-agent-bind/agent_bind.cc
new file mode 100644
index 0000000..44366c1
--- /dev/null
+++ b/test/987-agent-bind/agent_bind.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <memory>
+#include <stdio.h>
+#include <dlfcn.h>
+
+#include "android-base/stringprintf.h"
+#include "jni.h"
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "scoped_local_ref.h"
+
+namespace art {
+namespace Test987AgentBind {
+
+static void doUpPrintCall(JNIEnv* env, const char* function) {
+ ScopedLocalRef<jclass> klass(env, env->FindClass("art/Test987"));
+ jmethodID targetMethod = env->GetStaticMethodID(klass.get(), function, "()V");
+ env->CallStaticVoidMethod(klass.get(), targetMethod);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test987_00024Transform_sayHi__(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ doUpPrintCall(env, "doSayHi");
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test987_00024Transform_sayHi2(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ doUpPrintCall(env, "doSayHi2");
+}
+
+} // namespace Test987AgentBind
+} // namespace art
diff --git a/test/987-agent-bind/expected.txt b/test/987-agent-bind/expected.txt
new file mode 100644
index 0000000..ee4a424
--- /dev/null
+++ b/test/987-agent-bind/expected.txt
@@ -0,0 +1,2 @@
+Hello
+Hello - 2
diff --git a/test/987-agent-bind/info.txt b/test/987-agent-bind/info.txt
new file mode 100644
index 0000000..ae4a651
--- /dev/null
+++ b/test/987-agent-bind/info.txt
@@ -0,0 +1 @@
+Tests that native methods are bound from agent libs.
diff --git a/test/987-agent-bind/run b/test/987-agent-bind/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/987-agent-bind/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/987-agent-bind/src/Main.java b/test/987-agent-bind/src/Main.java
new file mode 100644
index 0000000..9ce6242
--- /dev/null
+++ b/test/987-agent-bind/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test987.run();
+ }
+}
diff --git a/test/987-agent-bind/src/art/Main.java b/test/987-agent-bind/src/art/Main.java
new file mode 100644
index 0000000..8b01920
--- /dev/null
+++ b/test/987-agent-bind/src/art/Main.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+// Binder class so the agent's C code has something that can be bound and exposed to tests.
+// In a package to separate cleanly and work around CTS reference issues (though this class
+// should be replaced in the CTS version).
+public class Main {
+ // Load the given class with the given classloader, and bind all native methods to corresponding
+ // C methods in the agent. Will abort if any of the steps fail.
+ public static native void bindAgentJNI(String className, ClassLoader classLoader);
+ // Same as above, giving the class directly.
+ public static native void bindAgentJNIForClass(Class<?> klass);
+}
diff --git a/test/987-agent-bind/src/art/Test987.java b/test/987-agent-bind/src/art/Test987.java
new file mode 100644
index 0000000..ae97ff2
--- /dev/null
+++ b/test/987-agent-bind/src/art/Test987.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Test987 {
+ // A class with a native method we can play with.
+ static class Transform {
+ private static native void sayHi();
+ private static native void sayHi2();
+ }
+
+ public static void run() throws Exception {
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ Transform.sayHi();
+ Transform.sayHi2();
+ }
+ // Functions called from native code.
+ public static void doSayHi() {
+ System.out.println("Hello");
+ }
+
+ public static void doSayHi2() {
+ System.out.println("Hello - 2");
+ }
+}
diff --git a/test/Android.bp b/test/Android.bp
index d8a3f0e..2b711cd 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -277,6 +277,7 @@
"945-obsolete-native/obsolete_native.cc",
"984-obsolete-invoke/obsolete_invoke.cc",
"986-native-method-bind/native_bind.cc",
+ "987-agent-bind/agent_bind.cc",
],
shared_libs: [
"libbase",