1 // Copyright 2015 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/java_exception_reporter.h"
6
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.h"
9 #include "base/android/scoped_java_ref.h"
10 #include "base/base_jni/JavaExceptionReporter_jni.h"
11 #include "base/debug/dump_without_crashing.h"
12 #include "base/functional/bind.h"
13 #include "base/functional/callback.h"
14 #include "base/lazy_instance.h"
15 #include "base/logging.h"
16
17 using base::android::JavaParamRef;
18 using base::android::JavaRef;
19
20 namespace base {
21 namespace android {
22
23 namespace {
24
25 void (*g_java_exception_callback)(const char*);
26
27 using JavaExceptionFilter =
28 base::RepeatingCallback<bool(const JavaRef<jthrowable>&)>;
29
30 LazyInstance<JavaExceptionFilter>::Leaky g_java_exception_filter =
31 LAZY_INSTANCE_INITIALIZER;
32
33 } // namespace
34
InitJavaExceptionReporter()35 void InitJavaExceptionReporter() {
36 JNIEnv* env = base::android::AttachCurrentThread();
37 // Since JavaExceptionReporter#installHandler will chain through to the
38 // default handler, the default handler should cause a crash as if it's a
39 // normal java exception. Prefer to crash the browser process in java rather
40 // than native since for webview, the embedding app may have installed its
41 // own JavaExceptionReporter handler and would expect it to be called.
42 constexpr bool crash_after_report = false;
43 SetJavaExceptionFilter(
44 base::BindRepeating([](const JavaRef<jthrowable>&) { return true; }));
45 Java_JavaExceptionReporter_installHandler(env, crash_after_report);
46 }
47
InitJavaExceptionReporterForChildProcess()48 void InitJavaExceptionReporterForChildProcess() {
49 JNIEnv* env = base::android::AttachCurrentThread();
50 constexpr bool crash_after_report = true;
51 SetJavaExceptionFilter(
52 base::BindRepeating([](const JavaRef<jthrowable>&) { return true; }));
53 Java_JavaExceptionReporter_installHandler(env, crash_after_report);
54 }
55
SetJavaExceptionFilter(JavaExceptionFilter java_exception_filter)56 void SetJavaExceptionFilter(JavaExceptionFilter java_exception_filter) {
57 g_java_exception_filter.Get() = std::move(java_exception_filter);
58 }
59
SetJavaExceptionCallback(void (* callback)(const char *))60 void SetJavaExceptionCallback(void (*callback)(const char*)) {
61 DCHECK(!g_java_exception_callback);
62 g_java_exception_callback = callback;
63 }
64
SetJavaException(const char * exception)65 void SetJavaException(const char* exception) {
66 // No need to print exception because they are already logged via
67 // env->ExceptionDescribe() within jni_android.cc.
68 if (g_java_exception_callback) {
69 g_java_exception_callback(exception);
70 }
71 }
72
JNI_JavaExceptionReporter_ReportJavaException(JNIEnv * env,jboolean crash_after_report,const JavaParamRef<jthrowable> & e)73 void JNI_JavaExceptionReporter_ReportJavaException(
74 JNIEnv* env,
75 jboolean crash_after_report,
76 const JavaParamRef<jthrowable>& e) {
77 std::string exception_info = base::android::GetJavaExceptionInfo(env, e);
78 bool should_report_exception = g_java_exception_filter.Get().Run(e);
79 if (should_report_exception) {
80 SetJavaException(exception_info.c_str());
81 }
82 if (crash_after_report) {
83 LOG(ERROR) << exception_info;
84 LOG(FATAL) << "Uncaught exception";
85 }
86 if (should_report_exception) {
87 base::debug::DumpWithoutCrashing();
88 SetJavaException(nullptr);
89 }
90 }
91
JNI_JavaExceptionReporter_ReportJavaStackTrace(JNIEnv * env,const JavaParamRef<jstring> & stack_trace)92 void JNI_JavaExceptionReporter_ReportJavaStackTrace(
93 JNIEnv* env,
94 const JavaParamRef<jstring>& stack_trace) {
95 SetJavaException(ConvertJavaStringToUTF8(stack_trace).c_str());
96 base::debug::DumpWithoutCrashing();
97 SetJavaException(nullptr);
98 }
99
100 } // namespace android
101 } // namespace base
102