I'm trying to understand the lifecycle stages of an Android app with a native lib.
The native library is loaded like this in the MainActivity:
static {
System.loadLibrary("lifecycleevents");
}
The definition of JNI_onLoad and JNI_onUnLoad are as follows:
extern "C" JNIEXPORT jint JNICALL
JNI_OnLoad (JavaVM * pJavaVM, [[maybe_unused]] void * pReserved) {
JNIEnv * env;
if (pJavaVM->GetEnv ((void **) &env, JNI_VERSION_1_6) != JNI_OK)
return JNI_ERR;
__android_log_print (ANDROID_LOG_VERBOSE, (const char *) TAG, "JNI_OnLoad");
return env->GetVersion ();
}
extern "C" JNIEXPORT void JNICALL
JNI_OnUnload (JavaVM * pJavaVM, [[maybe_unused]] void * pReserved) {
__android_log_print (ANDROID_LOG_VERBOSE, (const char *) TAG, "JNI_OnUnload");
}
I'm able to trace JNI_onLoad but JNI_onUnLoad never hits.
According to the documentation, it will be called when the classloader of the native lib is garbage collected.
This will happen at some point after MainActivity's onDestroy but before the app terminates, right? So, why is JNI_onUnLoad not called?
Also, in the documentation, JNI_onUnLoad_L will be called when the classloader of the native lib L is garbage collected. Since my native lib name is lifecycleevents, I renamed the JNI_onUnLoad method to JNI_onUnLoad_lifecycleevents, but it still doesn't get called.
I'm confused. Are both the methods valid? Requesting help from anyone who is familiar with this topic.
Related
I had wrote a agent to use the JVMTI on android 9. Code like this, I create an AgentFunction object to monitor the VM. It work's fine.
AgentFunction *agent = 0;
JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char *options, void *reserved) {
agent = new AgentFunction();
agent->Init(vm);
agent->ParseOptions(options);
agent->AddCapability();
agent->ShowCapabilities();
agent->RegisterEvent();
return JNI_OK;
}
Then i want export some interface to java, than user can invoke the JVMTI function directly.
private native boolean applyChangeNative(List<ClassInfo> classes);
The JNI fumction in agent.so
extern "C"
JNIEXPORT jboolean JNICALL Java_com_cc_jvmtiagent_JVMTIFunction_applyChangeNative
(JNIEnv *jniEnv, jobject, jlong jvmti, jobject classInfo) {
...
jvmtiClassDefinition *def = new jvmtiClassDefinition[total_classes];
agent->RedefineClasses(total_classes, def);
}
But when invoke the native method applyChangeNative from JAVA, the agent->RedefineClasses crash caused by agent is null. After my test, i found i can't access the object create in JVMTI from JNI.
I had read the JDK souce code , I found it have an InvocationAdapter.cc, When Agent_OnAttach it create the JPLISAgent, then create java.lang.instrument.Instrumentation and save the JPLISAgent in it. Each function from Java will take the JPLISAgent point.
But i want to known , why access the JVMTI object is NULL directly from JNI?
Resolved:
If you want invoke the agent method via JNI, you should use System.Load(agentPath) instead of System.LoadLibrary(libName). It need use the same so file.
It work's on Android 9 and 10, But on Android 8.x, Can't access the agent, i do not know why.
I've been fiddling with JVMTI API, trying to write something useful. Pasting code from other SO questions together, I've attempted to create a simple tool which would dump the heap when some particular kind of exception occurs, possibly useful in environments where creating jdb listener would be unwieldy. Mentioned code:
#include <jni.h>
#include <jvmti.h>
#include <string.h>
#include <stdio.h>
#include "jmm.h"
JNIEXPORT void* JNICALL JVM_GetManagement(jint version);
jvmtiEnv* jvmti;
void JNICALL
Exception(jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method,
jlocation location,
jobject exception,
jmethodID catch_method,
jlocation catch_location) {
char *exception_signature;
/* Obtain signature of the exception and compare type name with FNFE */
jclass class = (*jni_env)->GetObjectClass(jni_env, exception);
(*jvmti)->GetClassSignature(jvmti_env, class, &exception_signature, NULL);
if (strcmp("Ljava/io/FileNotFoundException;", exception_signature)==0) {
JmmInterface* jmm = (JmmInterface*) JVM_GetManagement(JMM_VERSION_1_0);
if (jmm == NULL) {
printf("Sorry, JMM is not supported\n");
} else {
jstring path = (*jni)->NewStringUTF(jni, "dump.hprof");
jmm->DumpHeap0(jni, path, JNI_TRUE);
printf("Heap dumped\n");
}
}
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
(*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_0);
jvmtiEventCallbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.Exception = Exception;
(*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
(*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, NULL)
return 0;
}
The process as such is working; I get a heap dump while trying this code on a simple test program (which prints a few lines and generates exception between them). The problem is, thread stack analysis in Eclipse MAT shows me stack trace with DestroyJavaVM instead of main method, unlike heap dump generated with jmap. I expected that heap dump to be different from the jmap one, as it's a JNI call 'interrupting' the normal execution of the instrumented thread, but not disrupting the whole trace, just with native calls lying atop the stack.
Why is that so, and can I somehow prevent the issue, and dump the heap the way I'd like it to? Or is my entire reasoning wrong and it's not possible?
I've got a package called com.example.remote in that package I have a Remote.java file with
public native static void send_feedback(String feedback);
static {
System.loadLibrary("com_example_remote_Remote");
}
then in my com_example_remote_Remote.c file I got this method:
JNIEXPORT void JNICALL Java_com_example_remote_Remote_send_feedback(JNIEnv *env, jclass clazz, jstring feedback)
Everything compiles. But when I do a call to the java function the program stops working and gives me:
12-19 12:21:31.183: E/AndroidRuntime(3196): FATAL EXCEPTION: main
12-19 12:21:31.183: E/AndroidRuntime(3196): java.lang.UnsatisfiedLinkError: Native method not found: com.example.remote.Remote.send_feedback:(Ljava/lang/String;)V
12-19 12:21:31.183: E/AndroidRuntime(3196): at com.example.remote.Remote.send_feedback(Native Method)
I think my syntax is conform the rules of JNI. I deleted the obj map once I thought maybe it would solve the problem. I rebuild the project and still gives me that error.
EDIT:
changed the name to sendFeedback and now I get this error:
12-19 12:49:26.335: A/libc(3280): Fatal signal 11 (SIGSEGV) at 0x00000cd0 (code=0), thread 3280 (.example.remote)
Add extern "C" in native method definition. i.e.
extern "C" JNIEXPORT void JNICALL Java_com_example_remote_Remote_send_feedback(JNIEnv *env, jclass clazz, jstring feedback){.......}
Or
If possible change MethodName in declaration and definition (in java file and .c file) because _ may be problem in method name. i.e.
public native static void sendFeedback(String feedback);
extern "C" JNIEXPORT void JNICALL Java_com_example_remote_Remote_sendFeedback(JNIEnv *env, jclass clazz, jstring feedback)
{
....
}
you may find a sample here
I've changed the name to sendFeedback so it's without the underscore. Also there was an error in the method what doesn't matter for this answer.
I have an Android project with a Java base Activity, Java JNI interface class, and native code. I have the proper
System.loadlibrary(_libraryname_);
In my Java interface class and from the logcat output:
11-29 15:11:20.737: D/dalvikvm(8940): No JNI_OnLoad found in /data/data/com.example.testjni/lib/libTestJNI.so 0x406ef030, skipping init
In my cpp file header:
extern "C" {
JNIEXPORT jint JNICALL JNIOnLoad(JavaVM *, void *);
}
In the cpp file:
JNIEXPORT jint JNICALL JNIOnLoad(JavaVM *vm, void *reserved)
{
LOGI("JNIOnLoad");
jvm = vm;
return JNI_VERSION_1_6; /* the required JNI version */
}
But still the OnLoad function never is called. I've tried uninstalling the app and re-installing it but it never runs.
Function signature is JNI_OnLoad. There is an underscore / _ between JNI and OnLoad.
I have this java method:
public static native void processBuffer(ByteBuffer bb);
with javah
JNIEXPORT void JNICALL Java_com_lan_factorial_IssmJni_processBuffer
(JNIEnv *env, jclass klass , jobject buf)
work perfectly
with Jni onload
static JNINativeMethod method_table[] = {
{"fac" , "(J)J" , (void *) factorial},
{"getBuffer", "()[D" , (void *) getBufferNative},
//{"processBuffer", "(Ljava/nio/ByteBuffer)V", (void *) fillBuffer}};
The others method in this table works, exept the last one which correspond to the method generate from javah above. And of course if I do JNI onload i will have a method call fillBuffer.
Can someone explain why javah works but not jni_onload. Did I do something wrong?
I have other methods using jni_onload so I want to stay away from javah.
Thanks
You have a missing semicolon in the method signature. It doesn't 'correspond' at all. Don't guess at native method signatures: use the output of javap -s. Cut and paste.