diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 84d49f325c8..d378ae0e9aa 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -55,10 +55,8 @@ using namespace mozilla::jni; using namespace mozilla::widget; AndroidBridge* AndroidBridge::sBridge = nullptr; -pthread_t AndroidBridge::sJavaUiThread = -1; -static unsigned sJavaEnvThreadIndex = 0; +pthread_t AndroidBridge::sJavaUiThread; static jobject sGlobalContext = nullptr; -static void JavaThreadDetachFunc(void *arg); // This is a dummy class that can be used in the template for android::sp class AndroidRefable { @@ -163,8 +161,6 @@ AndroidBridge::ConstructBridge(JNIEnv *jEnv, Object::Param clsLoader, Object::Pa */ putenv("NSS_DISABLE_UNLOAD=1"); - PR_NewThreadPrivateIndex(&sJavaEnvThreadIndex, JavaThreadDetachFunc); - MOZ_ASSERT(!sBridge); sBridge = new AndroidBridge; sBridge->Init(jEnv, clsLoader); // Success or crash @@ -184,10 +180,6 @@ void AndroidBridge::Init(JNIEnv *jEnv, Object::Param clsLoader) { ALOG_BRIDGE("AndroidBridge::Init"); - jEnv->GetJavaVM(&mJavaVM); - if (!mJavaVM) { - MOZ_CRASH(); // Nothing we can do here - } AutoLocalJNIFrame jniFrame(jEnv); @@ -196,8 +188,6 @@ AndroidBridge::Init(JNIEnv *jEnv, Object::Param clsLoader) jEnv, jEnv->GetObjectClass(clsLoader.Get()), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); - mJNIEnv = nullptr; - mThread = pthread_t(); mGLControllerObj = nullptr; mOpenedGraphicsLibraries = false; mHasNativeBitmapAccess = false; @@ -263,14 +253,10 @@ bool AndroidBridge::SetMainThread(pthread_t thr) { ALOG_BRIDGE("AndroidBridge::SetMainThread"); - if (thr) { - mThread = thr; - mJavaVM->GetEnv((void**) &mJNIEnv, JNI_VERSION_1_2); - return (bool) mJNIEnv; - } - mJNIEnv = nullptr; - mThread = pthread_t(); + if (thr) { + return true; + } // SetMainThread(0) is called on Gecko shutdown, // so we should clean up the bridge here. @@ -324,29 +310,6 @@ jstring AndroidBridge::NewJavaString(AutoLocalJNIFrame* frame, const nsACString& return NewJavaString(frame, NS_ConvertUTF8toUTF16(string)); } -extern "C" { - __attribute__ ((visibility("default"))) - JNIEnv * GetJNIForThread() - { - JNIEnv *jEnv = static_cast(PR_GetThreadPrivate(sJavaEnvThreadIndex)); - if (jEnv) { - return jEnv; - } - JavaVM *jVm = mozilla::AndroidBridge::GetVM(); - if (!jVm->GetEnv(reinterpret_cast(&jEnv), JNI_VERSION_1_2)) { - MOZ_ASSERT(jEnv); - return jEnv; - } - if (!jVm->AttachCurrentThread(&jEnv, nullptr)) { - MOZ_ASSERT(jEnv); - PR_SetThreadPrivate(sJavaEnvThreadIndex, jEnv); - return jEnv; - } - MOZ_CRASH(); - return nullptr; // unreachable - } -} - void AutoGlobalWrappedJavaObject::Dispose() { if (isNull()) { return; @@ -1636,27 +1599,6 @@ NS_IMETHODIMP nsAndroidBridge::IsContentDocumentDisplayed(bool *aRet) return NS_OK; } -// DO NOT USE THIS unless you need to access JNI from -// non-main threads. This is probably not what you want. -// Questions, ask blassey or dougt. - -static void -JavaThreadDetachFunc(void *arg) -{ - JNIEnv *env = (JNIEnv*) arg; - MOZ_ASSERT(env, "No JNIEnv on Gecko thread"); - if (!env) { - return; - } - JavaVM *vm = nullptr; - env->GetJavaVM(&vm); - MOZ_ASSERT(vm, "No JavaVM on Gecko thread"); - if (!vm) { - return; - } - vm->DetachCurrentThread(); -} - uint32_t AndroidBridge::GetScreenOrientation() { diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index 8d97b7fc02c..4911a1feea6 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -141,39 +141,6 @@ public: return sBridge; } - static JavaVM *GetVM() { - MOZ_ASSERT(sBridge); - return sBridge->mJavaVM; - } - - - static JNIEnv *GetJNIEnv() { - MOZ_ASSERT(sBridge); - if (MOZ_UNLIKELY(!pthread_equal(pthread_self(), sBridge->mThread))) { - MOZ_CRASH(); - } - MOZ_ASSERT(sBridge->mJNIEnv); - return sBridge->mJNIEnv; - } - - static bool HasEnv() { - return sBridge && sBridge->mJNIEnv; - } - - static bool ThrowException(JNIEnv *aEnv, const char *aClass, - const char *aMessage) { - - return jni::ThrowException(aEnv, aClass, aMessage); - } - - static bool ThrowException(JNIEnv *aEnv, const char *aMessage) { - return jni::ThrowException(aEnv, aMessage); - } - - static void HandleUncaughtException(JNIEnv *aEnv) { - jni::HandleUncaughtException(aEnv); - } - // The bridge needs to be constructed via ConstructBridge first, // and then once the Gecko main thread is spun up (Gecko side), // SetMainThread should be called which will create the JNIEnv for @@ -351,13 +318,6 @@ protected: static AndroidBridge* sBridge; nsTArray > mSmsRequests; - // the global JavaVM - JavaVM *mJavaVM; - - // the JNIEnv for the main thread - JNIEnv *mJNIEnv; - pthread_t mThread; - widget::GeckoLayerClient::GlobalRef mLayerClient; // the android.telephony.SmsMessage class diff --git a/widget/android/AndroidJNIWrapper.cpp b/widget/android/AndroidJNIWrapper.cpp index 15aaa6d9354..e69d2c63a3b 100644 --- a/widget/android/AndroidJNIWrapper.cpp +++ b/widget/android/AndroidJNIWrapper.cpp @@ -127,4 +127,11 @@ extern "C" { JNIEnv* jsjni_GetJNIForThread() { return GetJNIForThread(); } + + // For compatibility with JNI.jsm; some addons bundle their own JNI.jsm, + // so we cannot just change the function name used in JNI.jsm. + __attribute__ ((visibility("default"))) + JNIEnv* GetJNIForThread() { + return mozilla::jni::GetEnvForThread(); + } } diff --git a/widget/android/jni/Utils.cpp b/widget/android/jni/Utils.cpp index 33757a63b5a..1945b4d8769 100644 --- a/widget/android/jni/Utils.cpp +++ b/widget/android/jni/Utils.cpp @@ -1,6 +1,8 @@ #include "Utils.h" #include "Types.h" +#include + #include "mozilla/Assertions.h" #include "AndroidBridge.h" @@ -54,6 +56,67 @@ template<> const char TypedObject::name[] = "[D"; template<> const char TypedObject::name[] = "[Ljava/lang/Object;"; +JNIEnv* sGeckoThreadEnv; + +namespace { + +JavaVM* sJavaVM; +pthread_key_t sThreadEnvKey; + +void UnregisterThreadEnv(void* env) +{ + if (!env) { + // We were never attached. + return; + } + // The thread may have already been detached. In that case, it's still + // okay to call DetachCurrentThread(); it'll simply return an error. + // However, we must not access | env | because it may be invalid. + MOZ_ASSERT(sJavaVM); + sJavaVM->DetachCurrentThread(); +} + +} // namespace + +void SetGeckoThreadEnv(JNIEnv* aEnv) +{ + MOZ_ASSERT(aEnv); + MOZ_ASSERT(!sGeckoThreadEnv || sGeckoThreadEnv == aEnv); + + if (!sGeckoThreadEnv + && pthread_key_create(&sThreadEnvKey, UnregisterThreadEnv)) { + MOZ_CRASH("Failed to initialize required TLS"); + } + + sGeckoThreadEnv = aEnv; + MOZ_ALWAYS_TRUE(!pthread_setspecific(sThreadEnvKey, aEnv)); + + MOZ_ALWAYS_TRUE(!aEnv->GetJavaVM(&sJavaVM)); + MOZ_ASSERT(sJavaVM); +} + +JNIEnv* GetEnvForThread() +{ + MOZ_ASSERT(sGeckoThreadEnv); + + JNIEnv* env = static_cast(pthread_getspecific(sThreadEnvKey)); + if (env) { + return env; + } + + // We don't have a saved JNIEnv, so try to get one. + // AttachCurrentThread() does the same thing as GetEnv() when a thread is + // already attached, so we don't have to call GetEnv() at all. + if (!sJavaVM->AttachCurrentThread(&env, nullptr)) { + MOZ_ASSERT(env); + MOZ_ALWAYS_TRUE(!pthread_setspecific(sThreadEnvKey, env)); + return env; + } + + MOZ_CRASH("Failed to get JNIEnv for thread"); + return nullptr; // unreachable +} + bool ThrowException(JNIEnv *aEnv, const char *aClass, const char *aMessage) { diff --git a/widget/android/jni/Utils.h b/widget/android/jni/Utils.h index 36efff8c8e8..dac8e643b0a 100644 --- a/widget/android/jni/Utils.h +++ b/widget/android/jni/Utils.h @@ -3,14 +3,38 @@ #include -#include "mozilla/Types.h" - -/* See the comment in AndroidBridge about this function before using it */ -extern "C" MOZ_EXPORT JNIEnv * GetJNIForThread(); +#if defined(DEBUG) || !defined(RELEASE_BUILD) +#include "mozilla/Assertions.h" +#include "MainThreadUtils.h" +#endif namespace mozilla { namespace jni { +extern JNIEnv* sGeckoThreadEnv; + +inline bool IsAvailable() +{ + return !!sGeckoThreadEnv; +} + +inline JNIEnv* GetGeckoThreadEnv() +{ +#if defined(DEBUG) || !defined(RELEASE_BUILD) + if (!NS_IsMainThread()) { + MOZ_CRASH("Not on main thread"); + } + if (!sGeckoThreadEnv) { + MOZ_CRASH("Don't have a JNIEnv"); + } +#endif + return sGeckoThreadEnv; +} + +void SetGeckoThreadEnv(JNIEnv* aEnv); + +JNIEnv* GetEnvForThread(); + bool ThrowException(JNIEnv *aEnv, const char *aClass, const char *aMessage);