blob: ad636d6faf5994ee2d1d4a2675e79c7a2f3094ee [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base;
import android.os.DeadSystemException;
import androidx.annotation.UiThread;
import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
import org.jni_zero.JniType;
import org.jni_zero.NativeMethods;
/**
* This UncaughtExceptionHandler will create a breakpad minidump when there is an uncaught
* exception.
*
* <p>The exception's stack trace will be added to the minidump's data. This allows java-only
* crashes to be reported in the same way as other native crashes.
*/
@JNINamespace("base::android")
public class JavaExceptionReporter implements Thread.UncaughtExceptionHandler {
private final Thread.UncaughtExceptionHandler mParent;
private final boolean mCrashAfterReport;
private boolean mHandlingException;
private JavaExceptionReporter(
Thread.UncaughtExceptionHandler parent, boolean crashAfterReport) {
mParent = parent;
mCrashAfterReport = crashAfterReport;
}
/**
* Returns whether a given Throwable is meaningful and actionable and should be reported.
*
* <p>Removes the following exceptions:
*
* <ul>
* <li>DeadSystemException: The core Android system has died and is going through a restart.
* http://go/android-dev/reference/android/os/DeadSystemException
* </ul>
*/
public static boolean shouldReportThrowable(Throwable e) {
return !(e instanceof DeadSystemException);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
if (!mHandlingException && shouldReportThrowable(e)) {
mHandlingException = true;
JavaExceptionReporterJni.get()
.reportJavaException(
mCrashAfterReport,
// If we are dealing with a JNI uncaught exception, then `e` is just a
// wrapper around the true exception, annotated with the native stack
// trace. The native stack trace is redundant, since we're going to
// include it separately anyway. Remove it to make the report smaller,
// clearer and to prevent the true Java exception information from being
// truncated away.
e instanceof JniAndroid.UncaughtExceptionException ? e.getCause() : e);
}
if (mParent != null) {
mParent.uncaughtException(t, e);
}
}
/**
* Report and upload the stack trace as if it was a crash. This is very expensive and should
* be called rarely and only on the UI thread to avoid corrupting other crash uploads. Ideally
* only called in idle handlers.
*
* @param stackTrace The stack trace to report.
*/
@UiThread
public static void reportStackTrace(String stackTrace) {
assert ThreadUtils.runningOnUiThread();
JavaExceptionReporterJni.get()
.reportJavaStackTrace(PiiElider.sanitizeStacktrace(stackTrace));
}
/**
* Report and upload the stack trace as if it was a crash. This is very expensive and should
* be called rarely and only on the UI thread to avoid corrupting other crash uploads. Ideally
* only called in idle handlers.
*
* @param exception The exception to report.
*/
@UiThread
public static void reportException(Throwable exception) {
assert ThreadUtils.runningOnUiThread();
JavaExceptionReporterJni.get().reportJavaException(false, exception);
}
@CalledByNative
private static void installHandler(boolean crashAfterReport) {
Thread.setDefaultUncaughtExceptionHandler(
new JavaExceptionReporter(
Thread.getDefaultUncaughtExceptionHandler(), crashAfterReport));
}
@NativeMethods
interface Natives {
void reportJavaException(boolean crashAfterReport, Throwable e);
void reportJavaStackTrace(@JniType("std::string") String stackTrace);
}
}