Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android JNI: GetObjectClass crashes with SIGSEGV (not a valid JNI reference)

I am trying to create a new thread, and so I am passing the VM from my method initialize (called from Java) to my new thread. In the thread, I call AttachCurrentThread and get the JNIEnv* env.

Later on, I try to call GetObjectClass with the environment and it crashes. I believe this is because the object may not be initialize, but I am trying to call a method that is defined in the class that contains the native method. I have been trying to follow Section 4.2 (beginning) of http://java.sun.com/docs/books/jni/html/fldmeth.html.

Something very strange: I am testing with an HTC Dream running 2.2 and the following code does not crash, but with a Motorola Droid running 2.2.2 it does crash all the time!

This is my code:

C++

JNIEXPORT void JNICALL Java_com_device_client_HostConnection_initialize
  (JNIEnv * env, jobject obj, jint port) {

    JavaVM *vm;
    jint result = env->GetJavaVM(&vm);
    if (result < 0) {
        LOGE("Error using GetJavaVM\n");
        exit(-1);
    }

    struct javaInfo* data = (struct javaInfo*) malloc(sizeof(struct javaInfo));
    data->vm = vm;
    data->javaObjHost = obj;

    pthread_t pth;
    pthread_create(&pth, NULL, startServer, (void *) data);
}

The new thread:

void *startServer(void* arg) {
    jclass cls;
    jmethodID mid;
    JNIEnv* env = NULL;

    struct javaInfo* data = (struct javaInfo*) arg;
    JavaVM* vm = data->vm;
    jobject javaObjHost = data->javaObjHost;
    if (vm == NULL) {
        LOGE("VM is null\n");
    }
    vm->AttachCurrentThread(&env, NULL);

    cls = env->GetObjectClass(javaObjHost);
    mid = env->GetMethodID(cls, "setStatus", "(Z)V");
    if (mid == 0) {
        LOGD("ERROR: GetMethodID\n");
        exit(-1);
    }
    env->CallVoidMethod(javaObjHost, mid, false);

The line cls = env->GetObjectClass(javaObjHost); is crashing with the following output:

W/dalvikvm( 5827): JNI WARNING: 0x44872da0 is not a valid JNI reference
W/dalvikvm( 5827):              in Ldalvik/system/NativeStart;.run ()V (GetObjectClass)
I/dalvikvm( 5827): "Thread-9" prio=5 tid=8 RUNNABLE
I/dalvikvm( 5827):   | group="main" sCount=0 dsCount=0 s=N obj=0x448734c8 self=0x229da8
I/dalvikvm( 5827):   | sysTid=5834 nice=0 sched=0/0 cgrp=default handle=2265136
I/dalvikvm( 5827):   | schedstat=( 885010 3631591 2 )
I/dalvikvm( 5827):   at dalvik.system.NativeStart.run(Native Method)
I/dalvikvm( 5827): 
E/dalvikvm( 5827): VM aborting
I/DEBUG   ( 5511): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   ( 5511): Build fingerprint:  'verizon/voles/sholes/sholes:2.2.2/FRG83G/91102:user/release-keys'
I/DEBUG   ( 5511): pid: 5827, tid: 5834  >>> com.device.client <<<
I/DEBUG   ( 5511): signal 11 (SIGSEGV), fault addr deadd00d
....

My java code:

public class HostConnection {
    static {
        System.loadLibrary("hostConnection");
    }

    public void setStatus(boolean bool) {
        ...
    }

    public native void initialize(int defaultPort);
}

Could anyone please help me? I would like to call the setStatus() method inside my class, without creating a new object.

EDIT:

This is the new code, it is crashing currently crashing.

JNIEXPORT void JNICALL Java_com_device_client_HostConnection_initialize
  (JNIEnv * env, jobject obj, jint port) {
    JavaVM *vm;
    jint result = env->GetJavaVM(&vm);
    if (result < 0) {
        LOGE("Error using GetJavaVM\n");
        exit(-1);
    }

    jclass clsLocal = env->GetObjectClass(obj);
    if (clsLocal == NULL) {
        LOGE("ERROR: Cannot find class HostConnection\n");
        exit(-1);
    }

    jclass hostClass = (jclass) env->NewWeakGlobalRef(clsLocal);
    if (hostClass == NULL) {
        LOGE("ERROR: Run out of memory for weak global ref\n");
        exit(-1);
    }

    struct javaInfo* data = (struct javaInfo*) malloc(sizeof(struct javaInfo));
    data->vm = vm;
    data->hostClass = hostClass;

    pthread_t pth;
    pthread_create(&pth, NULL, startServer, (void *) data);
}

void *startServer(void* arg) {
    JNIEnv* env = NULL;
    jmethodID mid;
    struct fb_var_screeninfo vinfo;
    char server[MAXHOSTNAMELEN];

    struct javaInfo* data = (struct javaInfo*) arg;
    JavaVM* vm = data->vm;
    jclass hostClass = data->hostClass;
    if (vm == NULL) {
        LOGE("VM is null\n");
    }
    vm->AttachCurrentThread(&env, NULL);

    mid = env->GetMethodID(hostClass, "setStatus", "(Z)V");
    if (mid == 0) {
        LOGD("ERROR: GetMethodID\n");
        exit(-1);
    }
    env->CallVoidMethod(hostClass, mid, false);
}

I am getting the following crash:

W/dalvikvm( 7650): JNI WARNING: jclass points to invalid object 0xda88d23f
I/dalvikvm( 7650): "Thread-9" prio=5 tid=8 NATIVE
I/dalvikvm( 7650):   | group="main" sCount=0 dsCount=0 s=N obj=0x448734f8 self=0x228a20
I/dalvikvm( 7650):   | sysTid=7657 nice=0 sched=0/0 cgrp=default handle=2260192
I/dalvikvm( 7650):   | schedstat=( 854493 9155273 2 )
I/dalvikvm( 7650):   at dalvik.system.NativeStart.run(Native Method)
I/dalvikvm( 7650): 
E/dalvikvm( 7650): VM aborting
I/DEBUG   ( 5511): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   ( 5511): Build fingerprint: 'verizon/voles/sholes/sholes:2.2.2/FRG83G/91102:user/release-keys'
I/DEBUG   ( 5511): pid: 7650, tid: 7657  >>> com.device.client <<<
I/DEBUG   ( 5511): signal 11 (SIGSEGV), fault addr deadd00d

I know it crashes on GetMethodID(). I am really not sure why. Any help would really be greatly appreciated.

Thank you very much.

like image 641
Jary Avatar asked Oct 23 '11 05:10

Jary


1 Answers

You're getting the object from Java_com_device_client_HostConnection_initialize as a local reference, then you pass it to your new thread in your javaInfo struct.

Local reference are valid only in the current thread, in the same manner as JNIEnv* pointer.

Thus, when you get it from the struct in the new thread, you have an invalid reference to the object, which explains the crash.

Try to get a WeakGlobal reference in Java_com_device_client_HostConnection_initialize method using NewWeakGlobalRef method from JNIEnv.

Read chapter five of the JNI docs to understand the differences between local, global and weak global references.

like image 94
Geoffroy Avatar answered Sep 30 '22 13:09

Geoffroy