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.
5 #include "base/android/jni_android.h"
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"
24 JavaVM* g_jvm = nullptr;
25 jobject g_class_loader = nullptr;
26 jmethodID g_class_loader_load_class_method_id = 0;
28 bool g_fatal_exception_occurred = false;
30 ScopedJavaLocalRef<jclass> GetClassInternal(JNIEnv* env,
31 const char* class_name,
32 jobject class_loader) {
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] == '/') {
50 clazz = static_cast<jclass>(
51 env->CallObjectMethod(class_loader, g_class_loader_load_class_method_id,
52 ConvertUTF8ToJavaString(env, dotted_name).obj()));
54 clazz = env->FindClass(class_name);
56 if (ClearException(env) || !clazz) {
57 LOG(FATAL) << "Failed to find class " << class_name;
59 return ScopedJavaLocalRef<jclass>(env, clazz);
64 JNIEnv* AttachCurrentThread() {
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;
73 // 16 is the maximum size for thread names on Android.
75 int err = prctl(PR_GET_NAME, thread_name);
77 DPLOG(ERROR) << "prctl(PR_GET_NAME)";
80 args.name = thread_name;
83 #if BUILDFLAG(IS_ANDROID)
84 ret = g_jvm->AttachCurrentThread(&env, &args);
86 ret = g_jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), &args);
88 CHECK_EQ(JNI_OK, ret);
93 JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) {
95 JavaVMAttachArgs args;
96 args.version = JNI_VERSION_1_2;
97 args.name = const_cast<char*>(thread_name.c_str());
99 JNIEnv* env = nullptr;
100 #if BUILDFLAG(IS_ANDROID)
101 jint ret = g_jvm->AttachCurrentThread(&env, &args);
103 jint ret = g_jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), &args);
105 CHECK_EQ(JNI_OK, ret);
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.
113 g_jvm->DetachCurrentThread();
116 void InitVM(JavaVM* vm) {
117 DCHECK(!g_jvm || g_jvm == vm);
121 bool IsVMInitialized() {
122 return g_jvm != nullptr;
129 void DisableJvmForTesting() {
133 void InitGlobalClassLoader(JNIEnv* env) {
134 DCHECK(g_class_loader == nullptr);
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(),
142 "(Ljava/lang/String;)Ljava/lang/Class;");
143 CHECK(!ClearException(env));
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);
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));
157 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
158 return GetClassInternal(env, class_name, g_class_loader);
161 // This is duplicated with LazyGetClass below because these are performance
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);
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();
183 // This is duplicated with LazyGetClass above because these are performance
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);
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();
204 template<MethodID::Type type>
205 jmethodID MethodID::Get(JNIEnv* env,
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;
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,
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);
233 jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
234 atomic_method_id->store(id, std::memory_order_release);
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);
243 template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
244 JNIEnv* env, jclass clazz, const char* method_name,
245 const char* jni_signature);
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);
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);
255 bool HasException(JNIEnv* env) {
256 return env->ExceptionCheck() != JNI_FALSE;
259 bool ClearException(JNIEnv* env) {
260 if (!HasException(env))
262 env->ExceptionDescribe();
263 env->ExceptionClear();
267 void CheckException(JNIEnv* env) {
268 if (!HasException(env))
271 ScopedJavaLocalRef<jthrowable> throwable(env, env->ExceptionOccurred());
273 // Clear the pending exception, since a local reference is now held.
274 env->ExceptionDescribe();
275 env->ExceptionClear();
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");
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());
289 // Now, feel good about it and die.
290 LOG(FATAL) << "Please include Java exception stack in crash report";
293 std::string GetJavaExceptionInfo(JNIEnv* env,
294 const JavaRef<jthrowable>& throwable) {
295 ScopedJavaLocalRef<jstring> sanitized_exception_string =
296 Java_PiiElider_getSanitizedStacktrace(env, throwable);
298 return ConvertJavaStringToUTF8(sanitized_exception_string);
301 std::string GetJavaStackTraceIfPresent() {
302 JNIEnv* env = nullptr;
304 g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
307 // JNI has not been initialized on this thread.
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)
317 size_t newline_idx = ret.find('\n');
318 if (newline_idx == std::string::npos) {
319 // There are no java frames.
322 return ret.substr(newline_idx + 1);
325 } // namespace android