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