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