/* * BRLTTY - A background process providing access to the console screen (when in * text mode) for a blind person using a refreshable braille display. * * Copyright (C) 1995-2021 by The BRLTTY Developers. * * BRLTTY comes with ABSOLUTELY NO WARRANTY. * * This is free software, placed under the terms of the * GNU Lesser General Public License, as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any * later version. Please see the file LICENSE-LGPL for details. * * Web Page: http://brltty.app/ * * This software is maintained by Dave Mielke . */ #include "prologue.h" #include #include "log.h" #include "thread.h" #include "system.h" #include "system_java.h" static JavaVM *javaVirtualMachine = NULL; JNIEXPORT jint JNI_OnLoad (JavaVM *vm, void *reserved) { javaVirtualMachine = vm; return JAVA_JNI_VERSION; } JNIEXPORT void JNI_OnUnload (JavaVM *vm, void *reserved) { javaVirtualMachine = NULL; } JavaVM * getJavaInvocationInterface (void) { return javaVirtualMachine; } typedef struct { JavaVM *virtualMachine; JNIEnv *nativeInterface; char *threadName; } ThreadSpecificData; static THREAD_SPECIFIC_DATA_NEW(tsdJavaNativeThread) { ThreadSpecificData *tsd; if ((tsd = malloc(sizeof(*tsd)))) { memset(tsd, 0, sizeof(*tsd)); return tsd; } else { logMallocError(); } return NULL; } static THREAD_SPECIFIC_DATA_DESTROY(tsdJavaNativeThread) { ThreadSpecificData *tsd = data; { JavaVM *vm = tsd->virtualMachine; (*vm)->DetachCurrentThread(vm); } logMessage(LOG_DEBUG, "thread detached from Java VM: %s", tsd->threadName); free(tsd->threadName); free(tsd); } THREAD_SPECIFIC_DATA_CONTROL(tsdJavaNativeThread); static char * getJavaThreadName (JNIEnv *env) { char *name = NULL; static jclass Thread_class = NULL; if (findJavaClass(env, &Thread_class, JAVA_OBJ_THREAD)) { static jmethodID Thread_currentThread = 0; if (findJavaStaticMethod(env, &Thread_currentThread, Thread_class, "currentThread", JAVA_SIG_METHOD(JAVA_SIG_THREAD, ))) { jobject thread = (*env)->CallStaticObjectMethod(env, Thread_class, Thread_currentThread); if (!clearJavaException(env, 1)) { static jmethodID Thread_getName = 0; if (findJavaInstanceMethod(env, &Thread_getName, Thread_class, "getName", JAVA_SIG_METHOD(JAVA_SIG_STRING, ))) { jstring jName = (*env)->CallObjectMethod(env, thread, Thread_getName); if (!clearJavaException(env, 1)) { jboolean isCopy; const char *cName = (*env)->GetStringUTFChars(env, jName, &isCopy); if (!(name = strdup(cName))) { logMallocError(); } (*env)->ReleaseStringUTFChars(env, jName, cName); (*env)->DeleteLocalRef(env, jName); } } (*env)->DeleteLocalRef(env, thread); } } } return name; } JNIEnv * getJavaNativeInterface (void) { JavaVM *vm = getJavaInvocationInterface(); #ifdef __ANDROID__ JNIEnv *env = NULL; #else /* __ANDROID__ */ void *env = NULL; #endif /* __ANDROID__ */ if (vm) { jint result = (*vm)->GetEnv(vm, (void **)&env, JAVA_JNI_VERSION); if (result != JNI_OK) { if (result == JNI_EDETACHED) { JavaVMAttachArgs args = { .version = JAVA_JNI_VERSION, .name = NULL, .group = NULL }; if ((result = (*vm)->AttachCurrentThread(vm, &env, &args)) < 0) { logMessage(LOG_WARNING, "Java AttachCurrentThread error: %d", result); } else { ThreadSpecificData *tsd = getThreadSpecificData(&tsdJavaNativeThread); tsd->virtualMachine = vm; tsd->nativeInterface = env; tsd->threadName = getJavaThreadName(env); logMessage(LOG_DEBUG, "thread attached to Java VM: %s", tsd->threadName); } } else { logMessage(LOG_WARNING, "Java GetEnv error: %d", result); } } } return env; } int clearJavaException (JNIEnv *env, int describe) { int exceptionOccurred = (*env)->ExceptionCheck(env); if (exceptionOccurred) { if (describe) (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } return exceptionOccurred; } static jobject javaClassLoaderInstance = NULL; static jclass javaClassLoaderClass = NULL; static jmethodID loadClassMethod = 0; int setJavaClassLoader (JNIEnv *env, jobject instance) { if (instance) { javaClassLoaderInstance = (*env)->NewGlobalRef(env, instance); if (javaClassLoaderInstance) { jclass class = (*env)->GetObjectClass(env, instance); if (class) { javaClassLoaderClass = (*env)->NewGlobalRef(env, class); (*env)->DeleteLocalRef(env, class); class = NULL; if (javaClassLoaderClass) { jmethodID method = (*env)->GetMethodID(env, javaClassLoaderClass, "loadClass", JAVA_SIG_METHOD(JAVA_SIG_CLASS, JAVA_SIG_STRING // className )); if (method) { loadClassMethod = method; return 1; } (*env)->DeleteGlobalRef(env, javaClassLoaderClass); } } (*env)->DeleteGlobalRef(env, javaClassLoaderInstance); } } javaClassLoaderInstance = NULL; javaClassLoaderClass = NULL; loadClassMethod = 0; return 0; } static jclass loadJavaClass (JNIEnv *env, const char *path) { size_t size = strlen(path) + 1; char cName[size]; jclass class = NULL; jobject jName; { const char *p = path; char *n = cName; char c; do { c = *p++; if (c == '/') c = '.'; *n++ = c; } while (c); } if ((jName = (*env)->NewStringUTF(env, cName))) { jclass result = (*env)->CallObjectMethod(env, javaClassLoaderInstance, loadClassMethod, jName); if (clearJavaException(env, 1)) { (*env)->DeleteLocalRef(env, result); } else { class = result; } (*env)->DeleteLocalRef(env, jName); } else { logMallocError(); } return class; } int findJavaClass (JNIEnv *env, jclass *class, const char *path) { if (*class) return 1; { jclass localReference = loadClassMethod? loadJavaClass(env, path): (*env)->FindClass(env, path); if (localReference) { jclass globalReference = (*env)->NewGlobalRef(env, localReference); (*env)->DeleteLocalRef(env, localReference); localReference = NULL; if (globalReference) { logMessage(LOG_DEBUG, "java class found: %s", path); *class = globalReference; return 1; } else { logMallocError(); clearJavaException(env, 0); } } else { logMessage(LOG_ERR, "java class not found: %s", path); clearJavaException(env, 1); } } return 0; } int findJavaInstanceMethod ( JNIEnv *env, jmethodID *method, jclass class, const char *name, const char *signature ) { if (!*method) { if (!(*method = (*env)->GetMethodID(env, class, name, signature))) { logMessage(LOG_ERR, "java instance method not found: %s: %s", name, signature); clearJavaException(env, 0); return 0; } logMessage(LOG_DEBUG, "java instance method found: %s: %s", name, signature); } return 1; } int findJavaStaticMethod ( JNIEnv *env, jmethodID *method, jclass class, const char *name, const char *signature ) { if (!*method) { if (!(*method = (*env)->GetStaticMethodID(env, class, name, signature))) { logMessage(LOG_ERR, "java static method not found: %s: %s", name, signature); clearJavaException(env, 0); return 0; } logMessage(LOG_DEBUG, "java static method found: %s: %s", name, signature); } return 1; } int findJavaConstructor ( JNIEnv *env, jmethodID *constructor, jclass class, const char *signature ) { return findJavaInstanceMethod(env, constructor, class, JAVA_CONSTRUCTOR_NAME, signature); } int findJavaInstanceField ( JNIEnv *env, jfieldID *field, jclass class, const char *name, const char *signature ) { if (!*field) { if (!(*field = (*env)->GetFieldID(env, class, name, signature))) { logMessage(LOG_ERR, "java instance field not found: %s: %s", name, signature); clearJavaException(env, 0); return 0; } logMessage(LOG_DEBUG, "java instance field found: %s: %s", name, signature); } return 1; } int findJavaStaticField ( JNIEnv *env, jfieldID *field, jclass class, const char *name, const char *signature ) { if (!*field) { if (!(*field = (*env)->GetStaticFieldID(env, class, name, signature))) { logMessage(LOG_ERR, "java static field not found: %s: %s", name, signature); clearJavaException(env, 0); return 0; } logMessage(LOG_DEBUG, "java static field found: %s: %s", name, signature); } return 1; } char * getJavaLocaleName (void) { char *name = NULL; JNIEnv *env; if ((env = getJavaNativeInterface())) { jclass Locale_class = NULL; if (findJavaClass(env, &Locale_class, JAVA_OBJ_LOCALE)) { jmethodID Locale_getDefault = 0; if (findJavaStaticMethod(env, &Locale_getDefault, Locale_class, "getDefault", JAVA_SIG_METHOD(JAVA_SIG_LOCALE, ))) { jobject locale = (*env)->CallStaticObjectMethod(env, Locale_class, Locale_getDefault); if (!clearJavaException(env, 1)) { jmethodID Locale_toString = 0; if (findJavaInstanceMethod(env, &Locale_toString, Locale_class, "toString", JAVA_SIG_METHOD(JAVA_SIG_STRING, ))) { jstring jName = (*env)->CallObjectMethod(env, locale, Locale_toString); if (!clearJavaException(env, 1)) { jboolean isCopy; const char *cName = (*env)->GetStringUTFChars(env, jName, &isCopy); if (!(name = strdup(cName))) { logMallocError(); } (*env)->ReleaseStringUTFChars(env, jName, cName); (*env)->DeleteLocalRef(env, jName); } } (*env)->DeleteLocalRef(env, locale); } } } } return name; } #if defined(__ANDROID__) #include #include "messages.h" static void initializeAndroidEnvironment (JNIEnv *env) { { static jclass class = NULL; if (findJavaClass(env, &class, JAVA_OBJ_BRLTTY("BrailleApplication"))) { static jmethodID method = 0; if (findJavaStaticMethod(env, &method, class, "getCurrentLocale", JAVA_SIG_METHOD(JAVA_SIG_STRING, ))) { jstring jLocale = (*env)->CallStaticObjectMethod(env, class, method); if (!clearJavaException(env, 1)) { if (jLocale) { jboolean isCopy; const char *cLocale = (*env)->GetStringUTFChars(env, jLocale, &isCopy); if (cLocale) { if (setMessagesLocale(cLocale)) { } (*env)->ReleaseStringUTFChars(env, jLocale, cLocale); } (*env)->DeleteLocalRef(env, jLocale); } } } } } } #endif /* platform-speciofic initialization */ void initializeSystemObject (void) { #if defined(__ANDROID__) initializeAndroidEnvironment(getJavaNativeInterface()); #endif /* platform-speciofic initialization */ }