diff --git a/src/frontends/android/jni/libandroidbridge/android_jni.c b/src/frontends/android/jni/libandroidbridge/android_jni.c index 32957451f..0acebbbcd 100644 --- a/src/frontends/android/jni/libandroidbridge/android_jni.c +++ b/src/frontends/android/jni/libandroidbridge/android_jni.c @@ -18,6 +18,7 @@ #include "android_jni.h" #include +#include /** * JVM @@ -26,6 +27,40 @@ static JavaVM *android_jvm; jclass *android_charonvpnservice_class; +/** + * Thread-local variable. Only used because of the destructor + */ +static thread_value_t *androidjni_threadlocal; + +/** + * Thread-local destructor to ensure that a native thread is detached + * from the JVM even if androidjni_detach_thread() is not called. + */ +static void attached_thread_cleanup(void *arg) +{ + (*android_jvm)->DetachCurrentThread(android_jvm); +} + +/* + * Described in header + */ +void androidjni_attach_thread(JNIEnv **env) +{ + (*android_jvm)->AttachCurrentThread(android_jvm, env, NULL); + /* use a thread-local value with a destructor that automatically detaches + * the thread from the JVM before it terminates, if not done manually */ + androidjni_threadlocal->set(androidjni_threadlocal, (void*)*env); +} + +/* + * Described in header + */ +void androidjni_detach_thread() +{ + androidjni_threadlocal->set(androidjni_threadlocal, NULL); + (*android_jvm)->DetachCurrentThread(android_jvm); +} + /** * Called when this library is loaded by the JVM */ @@ -40,6 +75,8 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) return -1; } + androidjni_threadlocal = thread_value_create(attached_thread_cleanup); + android_charonvpnservice_class = (*env)->NewGlobalRef(env, (*env)->FindClass(env, JNI_PACKAGE_STRING "/CharonVpnService")); @@ -47,3 +84,11 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) return JNI_VERSION_1_6; } +/** + * Called when this library is unloaded by the JVM + */ +void JNI_OnUnload(JavaVM *vm, void *reserved) +{ + androidjni_threadlocal->destroy(androidjni_threadlocal); +} + diff --git a/src/frontends/android/jni/libandroidbridge/android_jni.h b/src/frontends/android/jni/libandroidbridge/android_jni.h index 8f8d30299..2a8d3a71b 100644 --- a/src/frontends/android/jni/libandroidbridge/android_jni.h +++ b/src/frontends/android/jni/libandroidbridge/android_jni.h @@ -43,4 +43,23 @@ */ extern jclass *android_charonvpnservice_class; +/** + * Attach the current thread to the JVM + * + * As local JNI references are not freed until the thread detaches + * androidjni_detach_thread() should be called as soon as possible. + * If it is not called a thread-local destructor ensures that the + * thread is at least detached as soon as it terminates. + * + * @param env JNIEnv + */ +void androidjni_attach_thread(JNIEnv **env); + +/** + * Detach the current thread from the JVM + * + * Call this as soon as possible to ensure that local JNI references are freed. + */ +void androidjni_detach_thread(); + #endif /** ANDROID_JNI_H_ @}*/