/* * 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 #include #include #include #include #include "embed.h" #include "system_java.h" #if defined(__ANDROID__) #include #define LOG(...) __android_log_print(ANDROID_LOG_DEBUG, PACKAGE_TARNAME, __VA_ARGS__) #else /* LOG() */ #warning logging not supported #define LOG(...) #endif /* LOG() */ SYMBOL_POINTER(brlttyConstruct); SYMBOL_POINTER(setJavaClassLoader); SYMBOL_POINTER(brlttyDestruct); SYMBOL_POINTER(brlttyEnableInterrupt); SYMBOL_POINTER(brlttyDisableInterrupt); SYMBOL_POINTER(brlttyInterrupt); SYMBOL_POINTER(brlttyWait); SYMBOL_POINTER(changeLogLevel); SYMBOL_POINTER(changeLogCategories); SYMBOL_POINTER(changeTextTable); SYMBOL_POINTER(changeAttributesTable); SYMBOL_POINTER(changeContractionTable); SYMBOL_POINTER(changeKeyboardTable); SYMBOL_POINTER(restartBrailleDriver); SYMBOL_POINTER(changeBrailleDriver); SYMBOL_POINTER(changeBrailleParameters); SYMBOL_POINTER(changeBrailleDevice); SYMBOL_POINTER(restartSpeechDriver); SYMBOL_POINTER(changeSpeechDriver); SYMBOL_POINTER(changeSpeechParameters); SYMBOL_POINTER(restartScreenDriver); SYMBOL_POINTER(changeScreenDriver); SYMBOL_POINTER(changeScreenParameters); SYMBOL_POINTER(changeMessageLocale); SYMBOL_POINTER(showMessage); typedef struct { const char *name; void *pointer; } SymbolEntry; #define BEGIN_SYMBOL_TABLE static const SymbolEntry symbolTable[] = { #define END_SYMBOL_TABLE {.name=NULL} }; #define SYMBOL_ENTRY(symbol) {.name=#symbol, .pointer=&symbol##_p} BEGIN_SYMBOL_TABLE SYMBOL_ENTRY(brlttyConstruct), SYMBOL_ENTRY(setJavaClassLoader), SYMBOL_ENTRY(brlttyDestruct), SYMBOL_ENTRY(brlttyEnableInterrupt), SYMBOL_ENTRY(brlttyDisableInterrupt), SYMBOL_ENTRY(brlttyInterrupt), SYMBOL_ENTRY(brlttyWait), SYMBOL_ENTRY(changeLogLevel), SYMBOL_ENTRY(changeLogCategories), SYMBOL_ENTRY(changeTextTable), SYMBOL_ENTRY(changeAttributesTable), SYMBOL_ENTRY(changeContractionTable), SYMBOL_ENTRY(changeKeyboardTable), SYMBOL_ENTRY(restartBrailleDriver), SYMBOL_ENTRY(changeBrailleDriver), SYMBOL_ENTRY(changeBrailleParameters), SYMBOL_ENTRY(changeBrailleDevice), SYMBOL_ENTRY(restartSpeechDriver), SYMBOL_ENTRY(changeSpeechDriver), SYMBOL_ENTRY(changeSpeechParameters), SYMBOL_ENTRY(restartScreenDriver), SYMBOL_ENTRY(changeScreenDriver), SYMBOL_ENTRY(changeScreenParameters), SYMBOL_ENTRY(changeMessageLocale), SYMBOL_ENTRY(showMessage), END_SYMBOL_TABLE static void *coreHandle = NULL; static jobject jArgumentArray = NULL; static const char **cArgumentArray = NULL; static int cArgumentCount; static void reportProblem ( JNIEnv *env, const char *throwable, const char *format, ... ) PRINTF(3, 4); static void reportProblem ( JNIEnv *env, const char *throwable, const char *format, ... ) { char message[0X100]; { va_list arguments; va_start(arguments, format); vsnprintf(message, sizeof(message), format, arguments); va_end(arguments); } if (0) { FILE *stream = stderr; fprintf(stream, "%s\n", message); fflush(stream); } { jclass object = (*env)->FindClass(env, throwable); if (object) { (*env)->ThrowNew(env, object, message); (*env)->DeleteLocalRef(env, object); } } } static void reportOutOfMemory (JNIEnv *env, const char *description) { reportProblem(env, JAVA_OBJ_OUT_OF_MEMORY_ERROR, "cannot allocate %s", description); } static int prepareProgramArguments (JNIEnv *env, jstring arguments) { jsize count = (*env)->GetArrayLength(env, arguments); if ((jArgumentArray = (*env)->NewGlobalRef(env, arguments))) { if ((cArgumentArray = malloc((count + 2) * sizeof(*cArgumentArray)))) { cArgumentArray[0] = PACKAGE_TARNAME; cArgumentArray[count+1] = NULL; { unsigned int i; for (i=1; i<=count; i+=1) cArgumentArray[i] = NULL; for (i=1; i<=count; i+=1) { jstring jArgument = (*env)->GetObjectArrayElement(env, arguments, i-1); jboolean isCopy; const char *cArgument = (*env)->GetStringUTFChars(env, jArgument, &isCopy); (*env)->DeleteLocalRef(env, jArgument); jArgument = NULL; if (!cArgument) { reportOutOfMemory(env, "C argument string"); break; } cArgumentArray[i] = cArgument; } if (i > count) { cArgumentCount = count + 1; return 1; } } } else { reportOutOfMemory(env, "C argument array"); } } else { reportOutOfMemory(env, "Java arguments array global reference"); } return 0; } static int loadCoreLibrary (JNIEnv *env) { if (coreHandle) return 1; if ((coreHandle = dlopen("libbrltty_core.so", RTLD_NOW | RTLD_GLOBAL))) { int allFound = 1; const SymbolEntry *symbol = symbolTable; while (symbol->name) { const void **pointer = symbol->pointer; if ((*pointer = dlsym(coreHandle, symbol->name))) { LOG("core symbol: %s -> %p", symbol->name, *pointer); } else { LOG("core symbol not found: %s", symbol->name); allFound = 0; } symbol += 1; } if (allFound) return 1; } reportProblem(env, JAVA_OBJ_UNSATISFIED_LINK_ERROR, "%s", dlerror()); return 0; } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, coreConstruct, jint, jobjectArray arguments, jobject classLoader ) { if (prepareProgramArguments(env, arguments)) { if (loadCoreLibrary(env)) { setJavaClassLoader_p(env, classLoader); return brlttyConstruct_p(cArgumentCount, (char **)cArgumentArray); } } return PROG_EXIT_FATAL; } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, coreDestruct, jboolean ) { jboolean result = brlttyDestruct_p()? JNI_TRUE: JNI_FALSE; /* { const SymbolEntry *symbol = symbolTable; while (symbol->name) { const void **pointer = symbol->pointer; *pointer = NULL; symbol += 1; } } if (coreHandle) { dlclose(coreHandle); coreHandle = NULL; } */ if (jArgumentArray) { if (cArgumentArray) { unsigned int i = 0; while (cArgumentArray[++i]) { jstring jArgument = (*env)->GetObjectArrayElement(env, jArgumentArray, i-1); (*env)->ReleaseStringUTFChars(env, jArgument, cArgumentArray[i]); (*env)->DeleteLocalRef(env, jArgument); jArgument = NULL; } free(cArgumentArray); cArgumentArray = NULL; } (*env)->DeleteGlobalRef(env, jArgumentArray); jArgumentArray = NULL; } return result; } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, coreEnableInterrupt, jboolean ) { return brlttyEnableInterrupt_p()? JNI_TRUE: JNI_FALSE; } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, coreDisableInterrupt, jboolean ) { return brlttyDisableInterrupt_p()? JNI_TRUE: JNI_FALSE; } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, coreInterrupt, jboolean, jboolean stop ) { return brlttyInterrupt_p((stop != JNI_FALSE)? WAIT_STOP: WAIT_CONTINUE)? JNI_TRUE: JNI_FALSE; } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, coreWait, jboolean, jint duration ) { return (brlttyWait_p(duration) != WAIT_STOP)? JNI_TRUE: JNI_FALSE; } static jboolean changeStringValue (JNIEnv *env, int (*change) (const char *cValue), jstring jValue) { jboolean result = JNI_FALSE; const char *cValue = (*env)->GetStringUTFChars(env, jValue, NULL); if (cValue) { if (change(cValue)) result = JNI_TRUE; (*env)->ReleaseStringUTFChars(env, jValue, cValue); } else { reportOutOfMemory(env, "C new value string"); } return result; } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeLogLevel, jboolean, jstring level ) { return changeStringValue(env, changeLogLevel_p, level); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeLogCategories, jboolean, jstring categories ) { return changeStringValue(env, changeLogCategories_p, categories); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeTextTable, jboolean, jstring name ) { return changeStringValue(env, changeTextTable_p, name); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeAttributesTable, jboolean, jstring name ) { return changeStringValue(env, changeAttributesTable_p, name); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeContractionTable, jboolean, jstring name ) { return changeStringValue(env, changeContractionTable_p, name); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeKeyboardTable, jboolean, jstring name ) { return changeStringValue(env, changeKeyboardTable_p, name); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, restartBrailleDriver, jboolean ) { restartBrailleDriver_p(); return JNI_TRUE; } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeBrailleDriver, jboolean, jstring driver ) { return changeStringValue(env, changeBrailleDriver_p, driver); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeBrailleParameters, jboolean, jstring parameters ) { return changeStringValue(env, changeBrailleParameters_p, parameters); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeBrailleDevice, jboolean, jstring device ) { return changeStringValue(env, changeBrailleDevice_p, device); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, restartSpeechDriver, jboolean ) { restartSpeechDriver_p(); return JNI_TRUE; } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeSpeechDriver, jboolean, jstring driver ) { return changeStringValue(env, changeSpeechDriver_p, driver); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeSpeechParameters, jboolean, jstring parameters ) { return changeStringValue(env, changeSpeechParameters_p, parameters); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, restartScreenDriver, jboolean ) { restartScreenDriver_p(); return JNI_TRUE; } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeScreenDriver, jboolean, jstring driver ) { return changeStringValue(env, changeScreenDriver_p, driver); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeScreenParameters, jboolean, jstring parameters ) { return changeStringValue(env, changeScreenParameters_p, parameters); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, changeMessageLocale, jboolean, jstring locale ) { return changeStringValue(env, changeMessageLocale_p, locale); } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, showMessage, void, jstring jMessage ) { const char *cMessage = (*env)->GetStringUTFChars(env, jMessage, NULL); if (cMessage) { showMessage_p(cMessage); (*env)->ReleaseStringUTFChars(env, jMessage, cMessage); } else { reportOutOfMemory(env, "C new value string"); } } JAVA_STATIC_METHOD ( org_a11y_brltty_core_CoreWrapper, setEnvironmentVariable, jboolean, jstring jName, jstring jValue ) { jboolean isCopy; const char *cName = (*env)->GetStringUTFChars(env, jName, &isCopy); const char *cValue = (*env)->GetStringUTFChars(env, jValue, &isCopy); int cResult = setenv(cName, cValue, 1) != -1; jboolean jResult = cResult? JNI_TRUE: JNI_FALSE; if (cResult) { LOG("environment variable set: %s: %s", cName, cValue); } else { LOG("environment variable not set: %s: %s", cName, strerror(errno)); } (*env)->ReleaseStringUTFChars(env, jName, cName); (*env)->ReleaseStringUTFChars(env, jValue, cValue); return jResult; } JNIEXPORT jint JNI_OnLoad (JavaVM *vm, void *reserved) { JNIEnv *env; if ((*vm)->GetEnv(vm, (void **)&env, JAVA_JNI_VERSION) == JNI_OK) { loadCoreLibrary(env); } return JAVA_JNI_VERSION; }