// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/android/java_exception_reporter.h" #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/android/scoped_java_ref.h" #include "base/base_jni_headers/JavaExceptionReporter_jni.h" #include "base/bind.h" #include "base/callback.h" #include "base/debug/dump_without_crashing.h" #include "base/lazy_instance.h" using base::android::JavaParamRef; using base::android::JavaRef; namespace base { namespace android { namespace { void (*g_java_exception_callback)(const char*); using JavaExceptionFilter = base::RepeatingCallback&)>; LazyInstance::Leaky g_java_exception_filter = LAZY_INSTANCE_INITIALIZER; } // namespace void InitJavaExceptionReporter() { JNIEnv* env = base::android::AttachCurrentThread(); // Since JavaExceptionReporter#installHandler will chain through to the // default handler, the default handler should cause a crash as if it's a // normal java exception. Prefer to crash the browser process in java rather // than native since for webview, the embedding app may have installed its // own JavaExceptionReporter handler and would expect it to be called. constexpr bool crash_after_report = false; SetJavaExceptionFilter( base::BindRepeating([](const JavaRef&) { return true; })); Java_JavaExceptionReporter_installHandler(env, crash_after_report); } void InitJavaExceptionReporterForChildProcess() { JNIEnv* env = base::android::AttachCurrentThread(); constexpr bool crash_after_report = true; SetJavaExceptionFilter( base::BindRepeating([](const JavaRef&) { return true; })); Java_JavaExceptionReporter_installHandler(env, crash_after_report); } void SetJavaExceptionFilter(JavaExceptionFilter java_exception_filter) { g_java_exception_filter.Get() = std::move(java_exception_filter); } void SetJavaExceptionCallback(void (*callback)(const char*)) { DCHECK(!g_java_exception_callback); g_java_exception_callback = callback; } void SetJavaException(const char* exception) { // No need to print exception because they are already logged via // env->ExceptionDescribe() within jni_android.cc. if (g_java_exception_callback) { g_java_exception_callback(exception); } } void JNI_JavaExceptionReporter_ReportJavaException( JNIEnv* env, jboolean crash_after_report, const JavaParamRef& e) { std::string exception_info = base::android::GetJavaExceptionInfo(env, e); bool should_report_exception = g_java_exception_filter.Get().Run(e); if (should_report_exception) { SetJavaException(exception_info.c_str()); } if (crash_after_report) { LOG(ERROR) << exception_info; LOG(FATAL) << "Uncaught exception"; } if (should_report_exception) { base::debug::DumpWithoutCrashing(); SetJavaException(nullptr); } } void JNI_JavaExceptionReporter_ReportJavaStackTrace( JNIEnv* env, const JavaParamRef& stack_trace) { SetJavaException(ConvertJavaStringToUTF8(stack_trace).c_str()); base::debug::DumpWithoutCrashing(); SetJavaException(nullptr); } } // namespace android } // namespace base