1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "Utils.h"
8 #include "Types.h"
9 
10 #include <android/log.h>
11 #include <pthread.h>
12 
13 #include "mozilla/Assertions.h"
14 #include "mozilla/java/GeckoAppShellWrappers.h"
15 #include "mozilla/java/GeckoThreadWrappers.h"
16 
17 #include "AndroidBuild.h"
18 #include "nsAppShell.h"
19 #include "nsExceptionHandler.h"
20 
21 namespace mozilla {
22 namespace jni {
23 
24 namespace detail {
25 
26 #define DEFINE_PRIMITIVE_TYPE_ADAPTER(NativeType, JNIType, JNIName, ABIName)   \
27                                                                                \
28   constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Call)(                  \
29       jobject, jmethodID, CallArgs::JValueType) MOZ_JNICALL_ABI;               \
30   constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticCall)(            \
31       jclass, jmethodID, CallArgs::JValueType) MOZ_JNICALL_ABI;                \
32   constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Get)(jobject, jfieldID) \
33       ABIName;                                                                 \
34   constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticGet)(             \
35       jclass, jfieldID) ABIName;                                               \
36   constexpr void (JNIEnv::*TypeAdapter<NativeType>::Set)(jobject, jfieldID,    \
37                                                          JNIType) ABIName;     \
38   constexpr void (JNIEnv::*TypeAdapter<NativeType>::StaticSet)(                \
39       jclass, jfieldID, JNIType) ABIName;                                      \
40   constexpr void (JNIEnv::*TypeAdapter<NativeType>::GetArray)(                 \
41       JNIType##Array, jsize, jsize, JNIType*)
42 
43 DEFINE_PRIMITIVE_TYPE_ADAPTER(bool, jboolean, Boolean, /*nothing*/);
44 DEFINE_PRIMITIVE_TYPE_ADAPTER(int8_t, jbyte, Byte, /*nothing*/);
45 DEFINE_PRIMITIVE_TYPE_ADAPTER(char16_t, jchar, Char, /*nothing*/);
46 DEFINE_PRIMITIVE_TYPE_ADAPTER(int16_t, jshort, Short, /*nothing*/);
47 DEFINE_PRIMITIVE_TYPE_ADAPTER(int32_t, jint, Int, /*nothing*/);
48 DEFINE_PRIMITIVE_TYPE_ADAPTER(int64_t, jlong, Long, /*nothing*/);
49 DEFINE_PRIMITIVE_TYPE_ADAPTER(float, jfloat, Float, MOZ_JNICALL_ABI);
50 DEFINE_PRIMITIVE_TYPE_ADAPTER(double, jdouble, Double, MOZ_JNICALL_ABI);
51 
52 #undef DEFINE_PRIMITIVE_TYPE_ADAPTER
53 
54 }  // namespace detail
55 
56 template <>
57 const char ObjectBase<Object, jobject>::name[] = "java/lang/Object";
58 template <>
59 const char ObjectBase<TypedObject<jstring>, jstring>::name[] =
60     "java/lang/String";
61 template <>
62 const char ObjectBase<TypedObject<jclass>, jclass>::name[] = "java/lang/Class";
63 template <>
64 const char ObjectBase<TypedObject<jthrowable>, jthrowable>::name[] =
65     "java/lang/Throwable";
66 template <>
67 const char ObjectBase<BoxedObject<jboolean>, jobject>::name[] =
68     "java/lang/Boolean";
69 template <>
70 const char ObjectBase<BoxedObject<jbyte>, jobject>::name[] = "java/lang/Byte";
71 template <>
72 const char ObjectBase<BoxedObject<jchar>, jobject>::name[] =
73     "java/lang/Character";
74 template <>
75 const char ObjectBase<BoxedObject<jshort>, jobject>::name[] = "java/lang/Short";
76 template <>
77 const char ObjectBase<BoxedObject<jint>, jobject>::name[] = "java/lang/Integer";
78 template <>
79 const char ObjectBase<BoxedObject<jlong>, jobject>::name[] = "java/lang/Long";
80 template <>
81 const char ObjectBase<BoxedObject<jfloat>, jobject>::name[] = "java/lang/Float";
82 template <>
83 const char ObjectBase<BoxedObject<jdouble>, jobject>::name[] =
84     "java/lang/Double";
85 template <>
86 const char ObjectBase<TypedObject<jbooleanArray>, jbooleanArray>::name[] = "[Z";
87 template <>
88 const char ObjectBase<TypedObject<jbyteArray>, jbyteArray>::name[] = "[B";
89 template <>
90 const char ObjectBase<TypedObject<jcharArray>, jcharArray>::name[] = "[C";
91 template <>
92 const char ObjectBase<TypedObject<jshortArray>, jshortArray>::name[] = "[S";
93 template <>
94 const char ObjectBase<TypedObject<jintArray>, jintArray>::name[] = "[I";
95 template <>
96 const char ObjectBase<TypedObject<jlongArray>, jlongArray>::name[] = "[J";
97 template <>
98 const char ObjectBase<TypedObject<jfloatArray>, jfloatArray>::name[] = "[F";
99 template <>
100 const char ObjectBase<TypedObject<jdoubleArray>, jdoubleArray>::name[] = "[D";
101 template <>
102 const char ObjectBase<TypedObject<jobjectArray>, jobjectArray>::name[] =
103     "[Ljava/lang/Object;";
104 template <>
105 const char ObjectBase<ByteBuffer, jobject>::name[] = "java/nio/ByteBuffer";
106 
107 JavaVM* sJavaVM;
108 JNIEnv* sGeckoThreadEnv;
109 
110 namespace {
111 
112 pthread_key_t sThreadEnvKey;
113 jclass sOOMErrorClass;
114 jobject sClassLoader;
115 jmethodID sClassLoaderLoadClass;
116 
UnregisterThreadEnv(void * env)117 void UnregisterThreadEnv(void* env) {
118   if (!env) {
119     // We were never attached.
120     return;
121   }
122   // The thread may have already been detached. In that case, it's still
123   // okay to call DetachCurrentThread(); it'll simply return an error.
124   // However, we must not access | env | because it may be invalid.
125   MOZ_ASSERT(sJavaVM);
126   sJavaVM->DetachCurrentThread();
127 }
128 
129 }  // namespace
130 
SetGeckoThreadEnv(JNIEnv * aEnv)131 void SetGeckoThreadEnv(JNIEnv* aEnv) {
132   MOZ_ASSERT(aEnv);
133   MOZ_ASSERT(!sGeckoThreadEnv || sGeckoThreadEnv == aEnv);
134 
135   if (!sGeckoThreadEnv &&
136       pthread_key_create(&sThreadEnvKey, UnregisterThreadEnv)) {
137     MOZ_CRASH("Failed to initialize required TLS");
138   }
139 
140   sGeckoThreadEnv = aEnv;
141   MOZ_ALWAYS_TRUE(!pthread_setspecific(sThreadEnvKey, aEnv));
142 
143   MOZ_ALWAYS_TRUE(!aEnv->GetJavaVM(&sJavaVM));
144   MOZ_ASSERT(sJavaVM);
145 
146   sOOMErrorClass =
147       Class::GlobalRef(
148           Class::LocalRef::Adopt(aEnv->FindClass("java/lang/OutOfMemoryError")))
149           .Forget();
150   aEnv->ExceptionClear();
151 
152   sClassLoader = Object::GlobalRef(java::GeckoThread::ClsLoader()).Forget();
153   sClassLoaderLoadClass = aEnv->GetMethodID(
154       Class::LocalRef::Adopt(aEnv->GetObjectClass(sClassLoader)).Get(),
155       "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
156   MOZ_ASSERT(sClassLoader && sClassLoaderLoadClass);
157 }
158 
GetEnvForThread()159 JNIEnv* GetEnvForThread() {
160   MOZ_ASSERT(sGeckoThreadEnv);
161 
162   JNIEnv* env = static_cast<JNIEnv*>(pthread_getspecific(sThreadEnvKey));
163   if (env) {
164     return env;
165   }
166 
167   // We don't have a saved JNIEnv, so try to get one.
168   // AttachCurrentThread() does the same thing as GetEnv() when a thread is
169   // already attached, so we don't have to call GetEnv() at all.
170   if (!sJavaVM->AttachCurrentThread(&env, nullptr)) {
171     MOZ_ASSERT(env);
172     MOZ_ALWAYS_TRUE(!pthread_setspecific(sThreadEnvKey, env));
173     return env;
174   }
175 
176   MOZ_CRASH("Failed to get JNIEnv for thread");
177   return nullptr;  // unreachable
178 }
179 
ThrowException(JNIEnv * aEnv,const char * aClass,const char * aMessage)180 bool ThrowException(JNIEnv* aEnv, const char* aClass, const char* aMessage) {
181   MOZ_ASSERT(aEnv, "Invalid thread JNI env");
182 
183   Class::LocalRef cls = Class::LocalRef::Adopt(aEnv->FindClass(aClass));
184   MOZ_ASSERT(cls, "Cannot find exception class");
185 
186   return !aEnv->ThrowNew(cls.Get(), aMessage);
187 }
188 
HandleUncaughtException(JNIEnv * aEnv)189 bool HandleUncaughtException(JNIEnv* aEnv) {
190   MOZ_ASSERT(aEnv, "Invalid thread JNI env");
191 
192   if (!aEnv->ExceptionCheck()) {
193     return false;
194   }
195 
196 #ifdef MOZ_CHECK_JNI
197   aEnv->ExceptionDescribe();
198 #endif
199 
200   Throwable::LocalRef e =
201       Throwable::LocalRef::Adopt(aEnv, aEnv->ExceptionOccurred());
202   MOZ_ASSERT(e);
203   aEnv->ExceptionClear();
204 
205   String::LocalRef stack = java::GeckoAppShell::GetExceptionStackTrace(e);
206   if (stack && ReportException(aEnv, e.Get(), stack.Get())) {
207     return true;
208   }
209 
210   aEnv->ExceptionClear();
211   java::GeckoAppShell::HandleUncaughtException(e);
212 
213   if (NS_WARN_IF(aEnv->ExceptionCheck())) {
214     aEnv->ExceptionDescribe();
215     aEnv->ExceptionClear();
216   }
217 
218   return true;
219 }
220 
ReportException(JNIEnv * aEnv,jthrowable aExc,jstring aStack)221 bool ReportException(JNIEnv* aEnv, jthrowable aExc, jstring aStack) {
222   bool result = true;
223 
224   result &= NS_SUCCEEDED(CrashReporter::AnnotateCrashReport(
225       CrashReporter::Annotation::JavaStackTrace,
226       String::Ref::From(aStack)->ToCString()));
227 
228   auto appNotes = java::GeckoAppShell::GetAppNotes();
229   if (NS_WARN_IF(aEnv->ExceptionCheck())) {
230     aEnv->ExceptionDescribe();
231     aEnv->ExceptionClear();
232   } else if (appNotes) {
233     CrashReporter::AppendAppNotesToCrashReport("\n"_ns + appNotes->ToCString());
234   }
235 
236   if (sOOMErrorClass && aEnv->IsInstanceOf(aExc, sOOMErrorClass)) {
237     NS_ABORT_OOM(0);  // Unknown OOM size
238   }
239   return result;
240 }
241 
242 namespace {
243 
244 jclass sJNIObjectClass;
245 jfieldID sJNIObjectHandleField;
246 
EnsureJNIObject(JNIEnv * env,jobject instance)247 bool EnsureJNIObject(JNIEnv* env, jobject instance) {
248   if (!sJNIObjectClass) {
249     sJNIObjectClass =
250         Class::GlobalRef(Class::LocalRef::Adopt(GetClassRef(
251                              env, "org/mozilla/gecko/mozglue/JNIObject")))
252             .Forget();
253 
254     sJNIObjectHandleField = env->GetFieldID(sJNIObjectClass, "mHandle", "J");
255   }
256 
257   MOZ_ASSERT(env->IsInstanceOf(instance, sJNIObjectClass),
258              "Java class is not derived from JNIObject");
259   return true;
260 }
261 
262 }  // namespace
263 
GetNativeHandle(JNIEnv * env,jobject instance)264 uintptr_t GetNativeHandle(JNIEnv* env, jobject instance) {
265   if (!EnsureJNIObject(env, instance)) {
266     return 0;
267   }
268 
269   return static_cast<uintptr_t>(
270       env->GetLongField(instance, sJNIObjectHandleField));
271 }
272 
SetNativeHandle(JNIEnv * env,jobject instance,uintptr_t handle)273 void SetNativeHandle(JNIEnv* env, jobject instance, uintptr_t handle) {
274   if (!EnsureJNIObject(env, instance)) {
275     return;
276   }
277 
278   env->SetLongField(instance, sJNIObjectHandleField,
279                     static_cast<jlong>(handle));
280 }
281 
GetClassRef(JNIEnv * aEnv,const char * aClassName)282 jclass GetClassRef(JNIEnv* aEnv, const char* aClassName) {
283   // First try the default class loader.
284   auto classRef = Class::LocalRef::Adopt(aEnv, aEnv->FindClass(aClassName));
285 
286   if ((!classRef || aEnv->ExceptionCheck()) && sClassLoader) {
287     // If the default class loader failed but we have an app class loader, try
288     // that. Clear the pending exception from failed FindClass call above.
289     aEnv->ExceptionClear();
290     classRef = Class::LocalRef::Adopt(
291         aEnv,
292         jclass(aEnv->CallObjectMethod(sClassLoader, sClassLoaderLoadClass,
293                                       StringParam(aClassName, aEnv).Get())));
294   }
295 
296   if (classRef && !aEnv->ExceptionCheck()) {
297     return classRef.Forget();
298   }
299 
300   __android_log_print(
301       ANDROID_LOG_ERROR, "Gecko",
302       ">>> FATAL JNI ERROR! FindClass(\"%s\") failed. "
303       "Does the class require a newer API version? "
304       "Or did ProGuard optimize away something it shouldn't have?",
305       aClassName);
306   aEnv->ExceptionDescribe();
307   MOZ_CRASH("Cannot find JNI class");
308   return nullptr;
309 }
310 
DispatchToGeckoPriorityQueue(already_AddRefed<nsIRunnable> aCall)311 void DispatchToGeckoPriorityQueue(already_AddRefed<nsIRunnable> aCall) {
312   class RunnableEvent : public nsAppShell::Event {
313     nsCOMPtr<nsIRunnable> mCall;
314 
315    public:
316     explicit RunnableEvent(already_AddRefed<nsIRunnable> aCall)
317         : mCall(aCall) {}
318     void Run() override { NS_ENSURE_SUCCESS_VOID(mCall->Run()); }
319   };
320 
321   nsAppShell::PostEvent(MakeUnique<RunnableEvent>(std::move(aCall)));
322 }
323 
GetAPIVersion()324 int GetAPIVersion() {
325   static int32_t apiVersion = 0;
326   if (!apiVersion && IsAvailable()) {
327     apiVersion = java::sdk::VERSION::SDK_INT();
328   }
329   return apiVersion;
330 }
331 
GetUIThreadId()332 pid_t GetUIThreadId() {
333   static pid_t uiThreadId;
334   if (!uiThreadId) {
335     uiThreadId = pid_t(java::GeckoThread::UiThreadId());
336   }
337   return uiThreadId;
338 }
339 
340 }  // namespace jni
341 }  // namespace mozilla
342