/* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "JavaVMHelper" #include "mediaplayer2/JavaVMHelper.h" #include <media/stagefright/foundation/ADebug.h> #include <utils/threads.h> #include <stdlib.h> 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; JavaVM *vm = sJavaVM.load(); CHECK(vm != NULL); if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) { return NULL; } 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