Search code examples
androidcandroid-ndkjava-native-interfacesdl-2

JNI calling non static function segfaults


I am doing some work with the SDL library for android but i'm having a little obstacle.

This function is defined in Java:

/**
 * This method is called by SDL using JNI.
 * @return an array which may be empty but is never null.
 */
public static int[] inputGetInputDeviceIds(int sources) {
    int[] ids = InputDevice.getDeviceIds();
    int[] filtered = new int[ids.length];
    int used = 0;
    for (int i = 0; i < ids.length; ++i) {
        InputDevice device = InputDevice.getDevice(ids[i]);
        if ((device != null) && ((device.getSources() & sources) != 0)) {
            filtered[used++] = device.getId();
        }
    }
    return Arrays.copyOf(filtered, used);
}

on the JNI/C side:

/* returns number of found touch devices as return value and ids in parameter ids */
int Android_JNI_GetTouchDeviceIds(int **ids) {
    JNIEnv *env = Android_JNI_GetEnv();
    jint sources = 4098; /* == InputDevice.SOURCE_TOUCHSCREEN */
    jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "inputGetInputDeviceIds", "(I)[I");
    jintArray array = (jintArray) (*env)->CallStaticObjectMethod(env, mActivityClass, mid, sources);
    int number = 0;
    *ids = NULL;
    if (array) {
        number = (int) (*env)->GetArrayLength(env, array);
        if (0 < number) {
            jint* elements = (*env)->GetIntArrayElements(env, array, NULL);
            if (elements) {
                int i;
                *ids = SDL_malloc(number * sizeof (**ids));
                for (i = 0; i < number; ++i) { /* not assuming sizeof (jint) == sizeof (int) */
                    (*ids)[i] = elements[i];
                }
                (*env)->ReleaseIntArrayElements(env, array, elements, JNI_ABORT);
            }
        }
        (*env)->DeleteLocalRef(env, array);
    }
    return number;
}

Now this works, but I would like to edit this to make the call non static. I changed the Java side code to this:

public int[] inputGetInputDeviceIds(int sources) {
    int[] ids = InputDevice.getDeviceIds();
    int[] filtered = new int[ids.length];
    int used = 0;
    for (int i = 0; i < ids.length; ++i) {
        InputDevice device = InputDevice.getDevice(ids[i]);
        if ((device != null) && ((device.getSources() & sources) != 0)) {
            filtered[used++] = device.getId();
        }
    }
    return Arrays.copyOf(filtered, used);
}

removing the static from the function name.

On the JNI side I do this:

/* returns number of found touch devices as return value and ids in parameter ids */
int Android_JNI_GetTouchDeviceIds(int **ids) {
    JNIEnv *env = Android_JNI_GetEnv();
    jint sources = 4098; /* == InputDevice.SOURCE_TOUCHSCREEN */
    //jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "inputGetInputDeviceIds", "(I)[I");
    //------------
    jclass clazz = (*env)->FindClass(env, "org/libsdl/app/SDLActivity");
    if (clazz == 0) {
        __android_log_print(ANDROID_LOG_INFO, "SDL", "find class failed SDL_android.c line 1409");
        return;
    }
    jmethodID javamethod = (*env)->GetMethodID(env, clazz, "inputGetInputDeviceIds", "(I)[I");
    if (javamethod == 0) {
        __android_log_print(ANDROID_LOG_INFO, "SDL", "find non static method failed SDL_android.c line 1414");
        return;
    }
    __android_log_print(ANDROID_LOG_INFO, "SDL", "SUCCESS GETTING JNI METHOD FROM ENV SDL_android.c line 1416"); <-- i reach this line successfully

    jintArray array = (*env)->CallObjectMethod(env, clazz, javamethod, sources); <-- this line segfaults 
    __android_log_print(ANDROID_LOG_INFO, "SDL", "made it to SDL_android.c line 1418");
    if(array)
    {
        __android_log_print(ANDROID_LOG_INFO, "SDL", "SUCCESS GETTING array line 1421");
    }
    else
    {
        __android_log_print(ANDROID_LOG_INFO, "SDL", "FAILED GETTING array line 1425");
    }
    //------------
    //jintArray array = (jintArray) (*env)->CallStaticObjectMethod(env, mActivityClass, mid, sources);
    int number = 0;
    *ids = NULL;
    if (array) {
        number = (int) (*env)->GetArrayLength(env, array);
        if (0 < number) {
            jint* elements = (*env)->GetIntArrayElements(env, array, NULL);
            if (elements) {
                int i;
                *ids = SDL_malloc(number * sizeof (**ids));
                for (i = 0; i < number; ++i) { /* not assuming sizeof (jint) == sizeof (int) */
                    (*ids)[i] = elements[i];
                }
                (*env)->ReleaseIntArrayElements(env, array, elements, JNI_ABORT);
            }
        }
        (*env)->DeleteLocalRef(env, array);
    }
    __android_log_print(ANDROID_LOG_INFO, "SDL", "reached the end of inputGetInputDeviceIds func");
    return number;
}

with this error:

09-19 22:40:53.514: A/libc(29636): Fatal signal 11 (SIGSEGV) at 0x00000001 (code=1), thread 29650 (Thread-22463)

I then recompile the c libs, clean the project and reinstall the app. The code segfaults at that line while the static version is fine. From looking around the code, there's no other static references in this function.

What could be causing that segfault? I've done a few tests and changing other static functions, grabbing the env getting the class, looking up the function and calling it actually works.

Any idea why this one will fail?


Solution

  • jintArray array = (*env)->CallObjectMethod(env, clazz, javamethod, sources);
                                                    ^^^^^   
                                             This is incorrect
    

    The second argument to CallObjectMethod shouldn't be a jclass, but a jobject refering to the instance of that class that you want to invoke the method on.
    So you'll need to pass the SDLActivity instance to your C function, so that the C function can pass it to CallObjectMethod.