summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/java_vm_ext.cc28
-rw-r--r--runtime/jni_internal.cc17
-rw-r--r--runtime/runtime.h4
-rw-r--r--runtime/ti/agent.cc14
-rw-r--r--runtime/ti/agent.h10
-rw-r--r--test/986-native-method-bind/expected.txt16
-rw-r--r--test/986-native-method-bind/native_bind.cc19
-rw-r--r--test/986-native-method-bind/src/art/Redefinition.java96
-rw-r--r--test/986-native-method-bind/src/art/Test986.java34
-rw-r--r--test/987-agent-bind/agent_bind.cc52
-rw-r--r--test/987-agent-bind/expected.txt2
-rw-r--r--test/987-agent-bind/info.txt1
-rwxr-xr-xtest/987-agent-bind/run17
-rw-r--r--test/987-agent-bind/src/Main.java21
-rw-r--r--test/987-agent-bind/src/art/Main.java28
-rw-r--r--test/987-agent-bind/src/art/Test987.java42
-rw-r--r--test/Android.bp1
17 files changed, 276 insertions, 126 deletions
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index b93b8f2a97..c8dc2f2d20 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 @@ class Libraries {
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 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
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 @@ void* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m) {
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 b146b51033..2626eefde2 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2159,10 +2159,11 @@ class JNI {
}
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 @@ class JNI {
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 @@ class JNI {
// 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 @@ class JNI {
<< "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 20db628a75..b91cb0c9be 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -657,6 +657,10 @@ class Runtime {
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 0bba44c988..86f5282664 100644
--- a/runtime/ti/agent.cc
+++ b/runtime/ti/agent.cc
@@ -72,6 +72,11 @@ Agent::LoadError Agent::DoLoadHelper(bool attaching,
}
}
+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 @@ Agent::LoadError Agent::DoDlOpen(/*out*/std::string* error_msg) {
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 7408aeec35..b5ecba19d9 100644
--- a/runtime/ti/agent.h
+++ b/runtime/ti/agent.h
@@ -29,8 +29,14 @@ namespace ti {
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 @@ class Agent {
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 189217d761..3376e6f91b 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 4f93f87dfe..eec635b2a0 100644
--- a/test/986-native-method-bind/native_bind.cc
+++ b/test/986-native-method-bind/native_bind.cc
@@ -38,11 +38,16 @@ static void doUpPrintCall(JNIEnv* env, const char* function) {
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_setNativeBindNotify(
}
}
+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 0350ab42ad..0000000000
--- 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 edd2e9d79f..aac73d33ab 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 @@ public class Test986 {
// 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 @@ public class Test986 {
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 class Test986 {
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 @@ public class Test986 {
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 0000000000..44366c1054
--- /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 0000000000..ee4a4247bd
--- /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 0000000000..ae4a651442
--- /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 0000000000..e92b873956
--- /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 0000000000..9ce6242c62
--- /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 0000000000..8b01920638
--- /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 0000000000..ae97ff25c3
--- /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 d8a3f0eeed..2b711cdc12 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -277,6 +277,7 @@ art_cc_defaults {
"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",