• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "JavaVMHelper"
18 
19 #include "mediaplayer2/JavaVMHelper.h"
20 
21 #include <media/stagefright/foundation/ADebug.h>
22 #include <utils/threads.h>
23 
24 #include <stdlib.h>
25 
26 namespace android {
27 
28 // static
29 std::atomic<JavaVM *> JavaVMHelper::sJavaVM(NULL);
30 
31 /*
32  * Makes the current thread visible to the VM.
33  *
34  * The JNIEnv pointer returned is only valid for the current thread, and
35  * thus must be tucked into thread-local storage.
36  */
javaAttachThread(const char * threadName,JNIEnv ** pEnv)37 static int javaAttachThread(const char* threadName, JNIEnv** pEnv) {
38     JavaVMAttachArgs args;
39     JavaVM* vm;
40     jint result;
41 
42     vm = JavaVMHelper::getJavaVM();
43     if (vm == NULL) {
44         return JNI_ERR;
45     }
46 
47     args.version = JNI_VERSION_1_4;
48     args.name = (char*) threadName;
49     args.group = NULL;
50 
51     result = vm->AttachCurrentThread(pEnv, (void*) &args);
52     if (result != JNI_OK) {
53         ALOGI("NOTE: attach of thread '%s' failed\n", threadName);
54     }
55 
56     return result;
57 }
58 
59 /*
60  * Detach the current thread from the set visible to the VM.
61  */
javaDetachThread(void)62 static int javaDetachThread(void) {
63     JavaVM* vm;
64     jint result;
65 
66     vm = JavaVMHelper::getJavaVM();
67     if (vm == NULL) {
68         return JNI_ERR;
69     }
70 
71     result = vm->DetachCurrentThread();
72     if (result != JNI_OK) {
73         ALOGE("ERROR: thread detach failed\n");
74     }
75     return result;
76 }
77 
78 /*
79  * When starting a native thread that will be visible from the VM, we
80  * bounce through this to get the right attach/detach action.
81  * Note that this function calls free(args)
82  */
javaThreadShell(void * args)83 static int javaThreadShell(void* args) {
84     void* start = ((void**)args)[0];
85     void* userData = ((void **)args)[1];
86     char* name = (char*) ((void **)args)[2];        // we own this storage
87     free(args);
88     JNIEnv* env;
89     int result;
90 
91     /* hook us into the VM */
92     if (javaAttachThread(name, &env) != JNI_OK) {
93         return -1;
94     }
95 
96     /* start the thread running */
97     result = (*(android_thread_func_t)start)(userData);
98 
99     /* unhook us */
100     javaDetachThread();
101     free(name);
102 
103     return result;
104 }
105 
106 /*
107  * This is invoked from androidCreateThreadEtc() via the callback
108  * set with androidSetCreateThreadFunc().
109  *
110  * We need to create the new thread in such a way that it gets hooked
111  * into the VM before it really starts executing.
112  */
javaCreateThreadEtc(android_thread_func_t entryFunction,void * userData,const char * threadName,int32_t threadPriority,size_t threadStackSize,android_thread_id_t * threadId)113 static int javaCreateThreadEtc(
114         android_thread_func_t entryFunction,
115         void* userData,
116         const char* threadName,
117         int32_t threadPriority,
118         size_t threadStackSize,
119         android_thread_id_t* threadId) {
120     void** args = (void**) malloc(3 * sizeof(void*));   // javaThreadShell must free
121     int result;
122 
123     LOG_ALWAYS_FATAL_IF(threadName == nullptr, "threadName not provided to javaCreateThreadEtc");
124 
125     args[0] = (void*) entryFunction;
126     args[1] = userData;
127     args[2] = (void*) strdup(threadName);   // javaThreadShell must free
128 
129     result = androidCreateRawThreadEtc(javaThreadShell, args,
130         threadName, threadPriority, threadStackSize, threadId);
131     return result;
132 }
133 
134 // static
getJNIEnv()135 JNIEnv *JavaVMHelper::getJNIEnv() {
136     JNIEnv *env;
137     JavaVM *vm = sJavaVM.load();
138     CHECK(vm != NULL);
139 
140     if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {
141         return NULL;
142     }
143 
144     return env;
145 }
146 
147 //static
getJavaVM()148 JavaVM *JavaVMHelper::getJavaVM() {
149     return sJavaVM.load();
150 }
151 
152 // static
setJavaVM(JavaVM * vm)153 void JavaVMHelper::setJavaVM(JavaVM *vm) {
154     sJavaVM.store(vm);
155 
156     // Ensure that Thread(/*canCallJava*/ true) in libutils is attached to the VM.
157     // This is supposed to be done by runtime, but when libutils is used with linker
158     // namespace, CreateThreadFunc should be initialized separately within the namespace.
159     androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
160 }
161 
162 }  // namespace android
163