/* * Copyright 2018 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "sdk/android/src/jni/jvm.h" #include #include #include #include #include #include #include "rtc_base/checks.h" namespace webrtc { namespace jni { static JavaVM* g_jvm = nullptr; static pthread_once_t g_jni_ptr_once = PTHREAD_ONCE_INIT; // Key for per-thread JNIEnv* data. Non-NULL in threads attached to `g_jvm` by // AttachCurrentThreadIfNeeded(), NULL in unattached threads and threads that // were attached by the JVM because of a Java->native call. static pthread_key_t g_jni_ptr; JavaVM* GetJVM() { RTC_CHECK(g_jvm) << "JNI_OnLoad failed to run?"; return g_jvm; } // Return a |JNIEnv*| usable on this thread or NULL if this thread is detached. JNIEnv* GetEnv() { void* env = nullptr; jint status = g_jvm->GetEnv(&env, JNI_VERSION_1_6); RTC_CHECK(((env != nullptr) && (status == JNI_OK)) || ((env == nullptr) && (status == JNI_EDETACHED))) << "Unexpected GetEnv return: " << status << ":" << env; return reinterpret_cast(env); } static void ThreadDestructor(void* prev_jni_ptr) { // This function only runs on threads where `g_jni_ptr` is non-NULL, meaning // we were responsible for originally attaching the thread, so are responsible // for detaching it now. However, because some JVM implementations (notably // Oracle's http://goo.gl/eHApYT) also use the pthread_key_create mechanism, // the JVMs accounting info for this thread may already be wiped out by the // time this is called. Thus it may appear we are already detached even though // it was our responsibility to detach! Oh well. if (!GetEnv()) return; RTC_CHECK(GetEnv() == prev_jni_ptr) << "Detaching from another thread: " << prev_jni_ptr << ":" << GetEnv(); jint status = g_jvm->DetachCurrentThread(); RTC_CHECK(status == JNI_OK) << "Failed to detach thread: " << status; RTC_CHECK(!GetEnv()) << "Detaching was a successful no-op???"; } static void CreateJNIPtrKey() { RTC_CHECK(!pthread_key_create(&g_jni_ptr, &ThreadDestructor)) << "pthread_key_create"; } jint InitGlobalJniVariables(JavaVM* jvm) { RTC_CHECK(!g_jvm) << "InitGlobalJniVariables!"; g_jvm = jvm; RTC_CHECK(g_jvm) << "InitGlobalJniVariables handed NULL?"; RTC_CHECK(!pthread_once(&g_jni_ptr_once, &CreateJNIPtrKey)) << "pthread_once"; JNIEnv* jni = nullptr; if (jvm->GetEnv(reinterpret_cast(&jni), JNI_VERSION_1_6) != JNI_OK) return -1; return JNI_VERSION_1_6; } // Return thread ID as a string. static std::string GetThreadId() { char buf[21]; // Big enough to hold a kuint64max plus terminating NULL. RTC_CHECK_LT(snprintf(buf, sizeof(buf), "%ld", static_cast(syscall(__NR_gettid))), sizeof(buf)) << "Thread id is bigger than uint64??"; return std::string(buf); } // Return the current thread's name. static std::string GetThreadName() { char name[17] = {0}; if (prctl(PR_GET_NAME, name) != 0) return std::string(""); return std::string(name); } // Return a |JNIEnv*| usable on this thread. Attaches to `g_jvm` if necessary. JNIEnv* AttachCurrentThreadIfNeeded() { JNIEnv* jni = GetEnv(); if (jni) return jni; RTC_CHECK(!pthread_getspecific(g_jni_ptr)) << "TLS has a JNIEnv* but not attached?"; std::string name(GetThreadName() + " - " + GetThreadId()); JavaVMAttachArgs args; args.version = JNI_VERSION_1_6; args.name = &name[0]; args.group = nullptr; // Deal with difference in signatures between Oracle's jni.h and Android's. #ifdef _JAVASOFT_JNI_H_ // Oracle's jni.h violates the JNI spec! void* env = nullptr; #else JNIEnv* env = nullptr; #endif RTC_CHECK(!g_jvm->AttachCurrentThread(&env, &args)) << "Failed to attach thread"; RTC_CHECK(env) << "AttachCurrentThread handed back NULL!"; jni = reinterpret_cast(env); RTC_CHECK(!pthread_setspecific(g_jni_ptr, jni)) << "pthread_setspecific"; return jni; } } // namespace jni } // namespace webrtc