Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7aafa219 authored by Dongwon Kang's avatar Dongwon Kang
Browse files

Ensure that Thread(/*canCallJava*/ true) is attached to the VM.

This is supposed to be done by runtime, but when libutils is
dual-loaded with linker namespace, CreateThreadFunc should be
initialized separately within the namespace since libutils keeps
it in a global variable.

Test: build & boot & atest MediaPlayer2Test
Bug: 112766913
Change-Id: I23eac262c8b88178bf151d7c6ab49a48af932001
parent 83af7a3c
Loading
Loading
Loading
Loading
+114 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include "mediaplayer2/JavaVMHelper.h"

#include <media/stagefright/foundation/ADebug.h>
#include <utils/threads.h>

#include <stdlib.h>

@@ -27,6 +28,109 @@ namespace android {
// static
std::atomic<JavaVM *> JavaVMHelper::sJavaVM(NULL);

/*
 * Makes the current thread visible to the VM.
 *
 * The JNIEnv pointer returned is only valid for the current thread, and
 * thus must be tucked into thread-local storage.
 */
static int javaAttachThread(const char* threadName, JNIEnv** pEnv) {
    JavaVMAttachArgs args;
    JavaVM* vm;
    jint result;

    vm = JavaVMHelper::getJavaVM();
    if (vm == NULL) {
        return JNI_ERR;
    }

    args.version = JNI_VERSION_1_4;
    args.name = (char*) threadName;
    args.group = NULL;

    result = vm->AttachCurrentThread(pEnv, (void*) &args);
    if (result != JNI_OK) {
        ALOGI("NOTE: attach of thread '%s' failed\n", threadName);
    }

    return result;
}

/*
 * Detach the current thread from the set visible to the VM.
 */
static int javaDetachThread(void) {
    JavaVM* vm;
    jint result;

    vm = JavaVMHelper::getJavaVM();
    if (vm == NULL) {
        return JNI_ERR;
    }

    result = vm->DetachCurrentThread();
    if (result != JNI_OK) {
        ALOGE("ERROR: thread detach failed\n");
    }
    return result;
}

/*
 * When starting a native thread that will be visible from the VM, we
 * bounce through this to get the right attach/detach action.
 * Note that this function calls free(args)
 */
static int javaThreadShell(void* args) {
    void* start = ((void**)args)[0];
    void* userData = ((void **)args)[1];
    char* name = (char*) ((void **)args)[2];        // we own this storage
    free(args);
    JNIEnv* env;
    int result;

    /* hook us into the VM */
    if (javaAttachThread(name, &env) != JNI_OK) {
        return -1;
    }

    /* start the thread running */
    result = (*(android_thread_func_t)start)(userData);

    /* unhook us */
    javaDetachThread();
    free(name);

    return result;
}

/*
 * This is invoked from androidCreateThreadEtc() via the callback
 * set with androidSetCreateThreadFunc().
 *
 * We need to create the new thread in such a way that it gets hooked
 * into the VM before it really starts executing.
 */
static int javaCreateThreadEtc(
        android_thread_func_t entryFunction,
        void* userData,
        const char* threadName,
        int32_t threadPriority,
        size_t threadStackSize,
        android_thread_id_t* threadId) {
    void** args = (void**) malloc(3 * sizeof(void*));   // javaThreadShell must free
    int result;

    LOG_ALWAYS_FATAL_IF(threadName == nullptr, "threadName not provided to javaCreateThreadEtc");

    args[0] = (void*) entryFunction;
    args[1] = userData;
    args[2] = (void*) strdup(threadName);   // javaThreadShell must free

    result = androidCreateRawThreadEtc(javaThreadShell, args,
        threadName, threadPriority, threadStackSize, threadId);
    return result;
}

// static
JNIEnv *JavaVMHelper::getJNIEnv() {
    JNIEnv *env;
@@ -40,9 +144,19 @@ JNIEnv *JavaVMHelper::getJNIEnv() {
    return env;
}

//static
JavaVM *JavaVMHelper::getJavaVM() {
    return sJavaVM.load();
}

// static
void JavaVMHelper::setJavaVM(JavaVM *vm) {
    sJavaVM.store(vm);

    // Ensure that Thread(/*canCallJava*/ true) in libutils is attached to the VM.
    // This is supposed to be done by runtime, but when libutils is used with linker
    // namespace, CreateThreadFunc should be initialized separately within the namespace.
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
}

}  // namespace android
+1 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ namespace android {

struct JavaVMHelper {
    static JNIEnv *getJNIEnv();
    static JavaVM *getJavaVM();
    static void setJavaVM(JavaVM *vm);

private: