| /* |
| * 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 |