Fix emulator build error
[platform/framework/web/chromium-efl.git] / base / android / jni_android.cc
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/android/jni_android.h"
6
7 #include <stddef.h>
8 #include <sys/prctl.h>
9
10 #include "base/android/java_exception_reporter.h"
11 #include "base/android/jni_string.h"
12 #include "base/android/jni_utils.h"
13 #include "base/android_runtime_jni_headers/Throwable_jni.h"
14 #include "base/base_jni/PiiElider_jni.h"
15 #include "base/debug/debugging_buildflags.h"
16 #include "base/logging.h"
17 #include "build/build_config.h"
18 #include "third_party/abseil-cpp/absl/base/attributes.h"
19
20 namespace base {
21 namespace android {
22 namespace {
23
24 JavaVM* g_jvm = nullptr;
25 jobject g_class_loader = nullptr;
26 jmethodID g_class_loader_load_class_method_id = 0;
27
28 bool g_fatal_exception_occurred = false;
29
30 ScopedJavaLocalRef<jclass> GetClassInternal(JNIEnv* env,
31                                             const char* class_name,
32                                             jobject class_loader) {
33   jclass clazz;
34   if (class_loader != nullptr) {
35     // ClassLoader.loadClass expects a classname with components separated by
36     // dots instead of the slashes that JNIEnv::FindClass expects. The JNI
37     // generator generates names with slashes, so we have to replace them here.
38     // TODO(torne): move to an approach where we always use ClassLoader except
39     // for the special case of base::android::GetClassLoader(), and change the
40     // JNI generator to generate dot-separated names. http://crbug.com/461773
41     size_t bufsize = strlen(class_name) + 1;
42     char dotted_name[bufsize];
43     memmove(dotted_name, class_name, bufsize);
44     for (size_t i = 0; i < bufsize; ++i) {
45       if (dotted_name[i] == '/') {
46         dotted_name[i] = '.';
47       }
48     }
49
50     clazz = static_cast<jclass>(
51         env->CallObjectMethod(class_loader, g_class_loader_load_class_method_id,
52                               ConvertUTF8ToJavaString(env, dotted_name).obj()));
53   } else {
54     clazz = env->FindClass(class_name);
55   }
56   if (ClearException(env) || !clazz) {
57     LOG(FATAL) << "Failed to find class " << class_name;
58   }
59   return ScopedJavaLocalRef<jclass>(env, clazz);
60 }
61
62 }  // namespace
63
64 JNIEnv* AttachCurrentThread() {
65   DCHECK(g_jvm);
66   JNIEnv* env = nullptr;
67   jint ret = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
68   if (ret == JNI_EDETACHED || !env) {
69     JavaVMAttachArgs args;
70     args.version = JNI_VERSION_1_2;
71     args.group = nullptr;
72
73     // 16 is the maximum size for thread names on Android.
74     char thread_name[16];
75     int err = prctl(PR_GET_NAME, thread_name);
76     if (err < 0) {
77       DPLOG(ERROR) << "prctl(PR_GET_NAME)";
78       args.name = nullptr;
79     } else {
80       args.name = thread_name;
81     }
82
83 #if BUILDFLAG(IS_ANDROID)
84     ret = g_jvm->AttachCurrentThread(&env, &args);
85 #else
86     ret = g_jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), &args);
87 #endif
88     CHECK_EQ(JNI_OK, ret);
89   }
90   return env;
91 }
92
93 JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) {
94   DCHECK(g_jvm);
95   JavaVMAttachArgs args;
96   args.version = JNI_VERSION_1_2;
97   args.name = const_cast<char*>(thread_name.c_str());
98   args.group = nullptr;
99   JNIEnv* env = nullptr;
100 #if BUILDFLAG(IS_ANDROID)
101   jint ret = g_jvm->AttachCurrentThread(&env, &args);
102 #else
103   jint ret = g_jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), &args);
104 #endif
105   CHECK_EQ(JNI_OK, ret);
106   return env;
107 }
108
109 void DetachFromVM() {
110   // Ignore the return value, if the thread is not attached, DetachCurrentThread
111   // will fail. But it is ok as the native thread may never be attached.
112   if (g_jvm)
113     g_jvm->DetachCurrentThread();
114 }
115
116 void InitVM(JavaVM* vm) {
117   DCHECK(!g_jvm || g_jvm == vm);
118   g_jvm = vm;
119 }
120
121 bool IsVMInitialized() {
122   return g_jvm != nullptr;
123 }
124
125 JavaVM* GetVM() {
126   return g_jvm;
127 }
128
129 void DisableJvmForTesting() {
130   g_jvm = nullptr;
131 }
132
133 void InitGlobalClassLoader(JNIEnv* env) {
134   DCHECK(g_class_loader == nullptr);
135
136   ScopedJavaLocalRef<jclass> class_loader_clazz =
137       GetClass(env, "java/lang/ClassLoader");
138   CHECK(!ClearException(env));
139   g_class_loader_load_class_method_id =
140       env->GetMethodID(class_loader_clazz.obj(),
141                        "loadClass",
142                        "(Ljava/lang/String;)Ljava/lang/Class;");
143   CHECK(!ClearException(env));
144
145   // GetClassLoader() caches the reference, so we do not need to wrap it in a
146   // smart pointer as well.
147   g_class_loader = GetClassLoader(env);
148 }
149
150 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env,
151                                     const char* class_name,
152                                     const char* split_name) {
153   return GetClassInternal(env, class_name,
154                           GetSplitClassLoader(env, split_name));
155 }
156
157 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
158   return GetClassInternal(env, class_name, g_class_loader);
159 }
160
161 // This is duplicated with LazyGetClass below because these are performance
162 // sensitive.
163 jclass LazyGetClass(JNIEnv* env,
164                     const char* class_name,
165                     const char* split_name,
166                     std::atomic<jclass>* atomic_class_id) {
167   const jclass value = atomic_class_id->load(std::memory_order_acquire);
168   if (value)
169     return value;
170   ScopedJavaGlobalRef<jclass> clazz;
171   clazz.Reset(GetClass(env, class_name, split_name));
172   jclass cas_result = nullptr;
173   if (atomic_class_id->compare_exchange_strong(cas_result, clazz.obj(),
174                                                std::memory_order_acq_rel)) {
175     // We intentionally leak the global ref since we now storing it as a raw
176     // pointer in |atomic_class_id|.
177     return clazz.Release();
178   } else {
179     return cas_result;
180   }
181 }
182
183 // This is duplicated with LazyGetClass above because these are performance
184 // sensitive.
185 jclass LazyGetClass(JNIEnv* env,
186                     const char* class_name,
187                     std::atomic<jclass>* atomic_class_id) {
188   const jclass value = atomic_class_id->load(std::memory_order_acquire);
189   if (value)
190     return value;
191   ScopedJavaGlobalRef<jclass> clazz;
192   clazz.Reset(GetClass(env, class_name));
193   jclass cas_result = nullptr;
194   if (atomic_class_id->compare_exchange_strong(cas_result, clazz.obj(),
195                                                std::memory_order_acq_rel)) {
196     // We intentionally leak the global ref since we now storing it as a raw
197     // pointer in |atomic_class_id|.
198     return clazz.Release();
199   } else {
200     return cas_result;
201   }
202 }
203
204 template<MethodID::Type type>
205 jmethodID MethodID::Get(JNIEnv* env,
206                         jclass clazz,
207                         const char* method_name,
208                         const char* jni_signature) {
209   auto get_method_ptr = type == MethodID::TYPE_STATIC ?
210       &JNIEnv::GetStaticMethodID :
211       &JNIEnv::GetMethodID;
212   jmethodID id = (env->*get_method_ptr)(clazz, method_name, jni_signature);
213   if (base::android::ClearException(env) || !id) {
214     LOG(FATAL) << "Failed to find " <<
215         (type == TYPE_STATIC ? "static " : "") <<
216         "method " << method_name << " " << jni_signature;
217   }
218   return id;
219 }
220
221 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
222 // into ::Get() above. If there's a race, it's ok since the values are the same
223 // (and the duplicated effort will happen only once).
224 template <MethodID::Type type>
225 jmethodID MethodID::LazyGet(JNIEnv* env,
226                             jclass clazz,
227                             const char* method_name,
228                             const char* jni_signature,
229                             std::atomic<jmethodID>* atomic_method_id) {
230   const jmethodID value = atomic_method_id->load(std::memory_order_acquire);
231   if (value)
232     return value;
233   jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
234   atomic_method_id->store(id, std::memory_order_release);
235   return id;
236 }
237
238 // Various template instantiations.
239 template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
240     JNIEnv* env, jclass clazz, const char* method_name,
241     const char* jni_signature);
242
243 template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
244     JNIEnv* env, jclass clazz, const char* method_name,
245     const char* jni_signature);
246
247 template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
248     JNIEnv* env, jclass clazz, const char* method_name,
249     const char* jni_signature, std::atomic<jmethodID>* atomic_method_id);
250
251 template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
252     JNIEnv* env, jclass clazz, const char* method_name,
253     const char* jni_signature, std::atomic<jmethodID>* atomic_method_id);
254
255 bool HasException(JNIEnv* env) {
256   return env->ExceptionCheck() != JNI_FALSE;
257 }
258
259 bool ClearException(JNIEnv* env) {
260   if (!HasException(env))
261     return false;
262   env->ExceptionDescribe();
263   env->ExceptionClear();
264   return true;
265 }
266
267 void CheckException(JNIEnv* env) {
268   if (!HasException(env))
269     return;
270
271   ScopedJavaLocalRef<jthrowable> throwable(env, env->ExceptionOccurred());
272   if (throwable) {
273     // Clear the pending exception, since a local reference is now held.
274     env->ExceptionDescribe();
275     env->ExceptionClear();
276
277     if (g_fatal_exception_occurred) {
278       // Another exception (probably OOM) occurred during GetJavaExceptionInfo.
279       base::android::SetJavaException(
280           "Java OOM'ed in exception handling, check logcat");
281     } else {
282       g_fatal_exception_occurred = true;
283       // RVO should avoid any extra copies of the exception string.
284       base::android::SetJavaException(
285           GetJavaExceptionInfo(env, throwable).c_str());
286     }
287   }
288
289   // Now, feel good about it and die.
290   LOG(FATAL) << "Please include Java exception stack in crash report";
291 }
292
293 std::string GetJavaExceptionInfo(JNIEnv* env,
294                                  const JavaRef<jthrowable>& throwable) {
295   ScopedJavaLocalRef<jstring> sanitized_exception_string =
296       Java_PiiElider_getSanitizedStacktrace(env, throwable);
297
298   return ConvertJavaStringToUTF8(sanitized_exception_string);
299 }
300
301 std::string GetJavaStackTraceIfPresent() {
302   JNIEnv* env = nullptr;
303   if (g_jvm) {
304     g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
305   }
306   if (!env) {
307     // JNI has not been initialized on this thread.
308     return {};
309   }
310   ScopedJavaLocalRef<jthrowable> throwable =
311       JNI_Throwable::Java_Throwable_Constructor(env);
312   std::string ret = GetJavaExceptionInfo(env, throwable);
313   // Strip the exception message and leave only the "at" lines. Example:
314   // java.lang.Throwable:
315   // {tab}at Clazz.method(Clazz.java:111)
316   // {tab}at ...
317   size_t newline_idx = ret.find('\n');
318   if (newline_idx == std::string::npos) {
319     // There are no java frames.
320     return {};
321   }
322   return ret.substr(newline_idx + 1);
323 }
324
325 }  // namespace android
326 }  // namespace base