xref: /aosp_15_r20/external/webrtc/sdk/android/src/jni/jvm.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker  *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker  *
4*d9f75844SAndroid Build Coastguard Worker  *  Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker  *  that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker  *  tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker  *  in the file PATENTS.  All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker  *  be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker  */
10*d9f75844SAndroid Build Coastguard Worker 
11*d9f75844SAndroid Build Coastguard Worker #include "sdk/android/src/jni/jvm.h"
12*d9f75844SAndroid Build Coastguard Worker 
13*d9f75844SAndroid Build Coastguard Worker #include <asm/unistd.h>
14*d9f75844SAndroid Build Coastguard Worker #include <pthread.h>
15*d9f75844SAndroid Build Coastguard Worker #include <sys/prctl.h>
16*d9f75844SAndroid Build Coastguard Worker #include <sys/syscall.h>
17*d9f75844SAndroid Build Coastguard Worker #include <unistd.h>
18*d9f75844SAndroid Build Coastguard Worker 
19*d9f75844SAndroid Build Coastguard Worker #include <string>
20*d9f75844SAndroid Build Coastguard Worker 
21*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
22*d9f75844SAndroid Build Coastguard Worker 
23*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
24*d9f75844SAndroid Build Coastguard Worker namespace jni {
25*d9f75844SAndroid Build Coastguard Worker 
26*d9f75844SAndroid Build Coastguard Worker static JavaVM* g_jvm = nullptr;
27*d9f75844SAndroid Build Coastguard Worker 
28*d9f75844SAndroid Build Coastguard Worker static pthread_once_t g_jni_ptr_once = PTHREAD_ONCE_INIT;
29*d9f75844SAndroid Build Coastguard Worker 
30*d9f75844SAndroid Build Coastguard Worker // Key for per-thread JNIEnv* data.  Non-NULL in threads attached to `g_jvm` by
31*d9f75844SAndroid Build Coastguard Worker // AttachCurrentThreadIfNeeded(), NULL in unattached threads and threads that
32*d9f75844SAndroid Build Coastguard Worker // were attached by the JVM because of a Java->native call.
33*d9f75844SAndroid Build Coastguard Worker static pthread_key_t g_jni_ptr;
34*d9f75844SAndroid Build Coastguard Worker 
GetJVM()35*d9f75844SAndroid Build Coastguard Worker JavaVM* GetJVM() {
36*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(g_jvm) << "JNI_OnLoad failed to run?";
37*d9f75844SAndroid Build Coastguard Worker   return g_jvm;
38*d9f75844SAndroid Build Coastguard Worker }
39*d9f75844SAndroid Build Coastguard Worker 
40*d9f75844SAndroid Build Coastguard Worker // Return a |JNIEnv*| usable on this thread or NULL if this thread is detached.
GetEnv()41*d9f75844SAndroid Build Coastguard Worker JNIEnv* GetEnv() {
42*d9f75844SAndroid Build Coastguard Worker   void* env = nullptr;
43*d9f75844SAndroid Build Coastguard Worker   jint status = g_jvm->GetEnv(&env, JNI_VERSION_1_6);
44*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(((env != nullptr) && (status == JNI_OK)) ||
45*d9f75844SAndroid Build Coastguard Worker             ((env == nullptr) && (status == JNI_EDETACHED)))
46*d9f75844SAndroid Build Coastguard Worker       << "Unexpected GetEnv return: " << status << ":" << env;
47*d9f75844SAndroid Build Coastguard Worker   return reinterpret_cast<JNIEnv*>(env);
48*d9f75844SAndroid Build Coastguard Worker }
49*d9f75844SAndroid Build Coastguard Worker 
ThreadDestructor(void * prev_jni_ptr)50*d9f75844SAndroid Build Coastguard Worker static void ThreadDestructor(void* prev_jni_ptr) {
51*d9f75844SAndroid Build Coastguard Worker   // This function only runs on threads where `g_jni_ptr` is non-NULL, meaning
52*d9f75844SAndroid Build Coastguard Worker   // we were responsible for originally attaching the thread, so are responsible
53*d9f75844SAndroid Build Coastguard Worker   // for detaching it now.  However, because some JVM implementations (notably
54*d9f75844SAndroid Build Coastguard Worker   // Oracle's http://goo.gl/eHApYT) also use the pthread_key_create mechanism,
55*d9f75844SAndroid Build Coastguard Worker   // the JVMs accounting info for this thread may already be wiped out by the
56*d9f75844SAndroid Build Coastguard Worker   // time this is called. Thus it may appear we are already detached even though
57*d9f75844SAndroid Build Coastguard Worker   // it was our responsibility to detach!  Oh well.
58*d9f75844SAndroid Build Coastguard Worker   if (!GetEnv())
59*d9f75844SAndroid Build Coastguard Worker     return;
60*d9f75844SAndroid Build Coastguard Worker 
61*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(GetEnv() == prev_jni_ptr)
62*d9f75844SAndroid Build Coastguard Worker       << "Detaching from another thread: " << prev_jni_ptr << ":" << GetEnv();
63*d9f75844SAndroid Build Coastguard Worker   jint status = g_jvm->DetachCurrentThread();
64*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(status == JNI_OK) << "Failed to detach thread: " << status;
65*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(!GetEnv()) << "Detaching was a successful no-op???";
66*d9f75844SAndroid Build Coastguard Worker }
67*d9f75844SAndroid Build Coastguard Worker 
CreateJNIPtrKey()68*d9f75844SAndroid Build Coastguard Worker static void CreateJNIPtrKey() {
69*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(!pthread_key_create(&g_jni_ptr, &ThreadDestructor))
70*d9f75844SAndroid Build Coastguard Worker       << "pthread_key_create";
71*d9f75844SAndroid Build Coastguard Worker }
72*d9f75844SAndroid Build Coastguard Worker 
InitGlobalJniVariables(JavaVM * jvm)73*d9f75844SAndroid Build Coastguard Worker jint InitGlobalJniVariables(JavaVM* jvm) {
74*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(!g_jvm) << "InitGlobalJniVariables!";
75*d9f75844SAndroid Build Coastguard Worker   g_jvm = jvm;
76*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(g_jvm) << "InitGlobalJniVariables handed NULL?";
77*d9f75844SAndroid Build Coastguard Worker 
78*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(!pthread_once(&g_jni_ptr_once, &CreateJNIPtrKey)) << "pthread_once";
79*d9f75844SAndroid Build Coastguard Worker 
80*d9f75844SAndroid Build Coastguard Worker   JNIEnv* jni = nullptr;
81*d9f75844SAndroid Build Coastguard Worker   if (jvm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_6) != JNI_OK)
82*d9f75844SAndroid Build Coastguard Worker     return -1;
83*d9f75844SAndroid Build Coastguard Worker 
84*d9f75844SAndroid Build Coastguard Worker   return JNI_VERSION_1_6;
85*d9f75844SAndroid Build Coastguard Worker }
86*d9f75844SAndroid Build Coastguard Worker 
87*d9f75844SAndroid Build Coastguard Worker // Return thread ID as a string.
GetThreadId()88*d9f75844SAndroid Build Coastguard Worker static std::string GetThreadId() {
89*d9f75844SAndroid Build Coastguard Worker   char buf[21];  // Big enough to hold a kuint64max plus terminating NULL.
90*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK_LT(snprintf(buf, sizeof(buf), "%ld",
91*d9f75844SAndroid Build Coastguard Worker                         static_cast<long>(syscall(__NR_gettid))),
92*d9f75844SAndroid Build Coastguard Worker                sizeof(buf))
93*d9f75844SAndroid Build Coastguard Worker       << "Thread id is bigger than uint64??";
94*d9f75844SAndroid Build Coastguard Worker   return std::string(buf);
95*d9f75844SAndroid Build Coastguard Worker }
96*d9f75844SAndroid Build Coastguard Worker 
97*d9f75844SAndroid Build Coastguard Worker // Return the current thread's name.
GetThreadName()98*d9f75844SAndroid Build Coastguard Worker static std::string GetThreadName() {
99*d9f75844SAndroid Build Coastguard Worker   char name[17] = {0};
100*d9f75844SAndroid Build Coastguard Worker   if (prctl(PR_GET_NAME, name) != 0)
101*d9f75844SAndroid Build Coastguard Worker     return std::string("<noname>");
102*d9f75844SAndroid Build Coastguard Worker   return std::string(name);
103*d9f75844SAndroid Build Coastguard Worker }
104*d9f75844SAndroid Build Coastguard Worker 
105*d9f75844SAndroid Build Coastguard Worker // Return a |JNIEnv*| usable on this thread.  Attaches to `g_jvm` if necessary.
AttachCurrentThreadIfNeeded()106*d9f75844SAndroid Build Coastguard Worker JNIEnv* AttachCurrentThreadIfNeeded() {
107*d9f75844SAndroid Build Coastguard Worker   JNIEnv* jni = GetEnv();
108*d9f75844SAndroid Build Coastguard Worker   if (jni)
109*d9f75844SAndroid Build Coastguard Worker     return jni;
110*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(!pthread_getspecific(g_jni_ptr))
111*d9f75844SAndroid Build Coastguard Worker       << "TLS has a JNIEnv* but not attached?";
112*d9f75844SAndroid Build Coastguard Worker 
113*d9f75844SAndroid Build Coastguard Worker   std::string name(GetThreadName() + " - " + GetThreadId());
114*d9f75844SAndroid Build Coastguard Worker   JavaVMAttachArgs args;
115*d9f75844SAndroid Build Coastguard Worker   args.version = JNI_VERSION_1_6;
116*d9f75844SAndroid Build Coastguard Worker   args.name = &name[0];
117*d9f75844SAndroid Build Coastguard Worker   args.group = nullptr;
118*d9f75844SAndroid Build Coastguard Worker // Deal with difference in signatures between Oracle's jni.h and Android's.
119*d9f75844SAndroid Build Coastguard Worker #ifdef _JAVASOFT_JNI_H_  // Oracle's jni.h violates the JNI spec!
120*d9f75844SAndroid Build Coastguard Worker   void* env = nullptr;
121*d9f75844SAndroid Build Coastguard Worker #else
122*d9f75844SAndroid Build Coastguard Worker   JNIEnv* env = nullptr;
123*d9f75844SAndroid Build Coastguard Worker #endif
124*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(!g_jvm->AttachCurrentThread(&env, &args))
125*d9f75844SAndroid Build Coastguard Worker       << "Failed to attach thread";
126*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(env) << "AttachCurrentThread handed back NULL!";
127*d9f75844SAndroid Build Coastguard Worker   jni = reinterpret_cast<JNIEnv*>(env);
128*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(!pthread_setspecific(g_jni_ptr, jni)) << "pthread_setspecific";
129*d9f75844SAndroid Build Coastguard Worker   return jni;
130*d9f75844SAndroid Build Coastguard Worker }
131*d9f75844SAndroid Build Coastguard Worker 
132*d9f75844SAndroid Build Coastguard Worker }  // namespace jni
133*d9f75844SAndroid Build Coastguard Worker }  // namespace webrtc
134