Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit addbf901 authored by Tobias Thierer's avatar Tobias Thierer
Browse files

Ensure apps cannot prevent uncaught exceptions being logged.

Let RuntimeInit use an UncaughtExceptionPreHandler to log an exception
rather than relying on UncaughtHandler, which apps can replace. This
makes it easier to diagnose application death, especially during app
compatibility testing for a new version of Android.

Test: Verified manually, with the help of a small sample app (not
checked in), that stacktraces for RuntimeExceptions thrown on main
or background threads are logged even when the app set a default
UncaughtExceptionHandler that swallows the exception with no action.

Note that such an inappropriate UncaughtExceptionHandler will still
cause threads to die without the app being killed, which it should be.
In an exception then happens on the main thread, the app will freeze
until the ANR dialog kicks in after a few seconds. I have manually
verified that this behavior is unchanged from before this CL.

No new integration tests are included because the default system
behavior has not changed.

Bug: 29624607
Change-Id: Ie87377b0bcadc3ba4083a8ab1bedb8f3dd95a4bd
parent 3f67a185
Loading
Loading
Loading
Loading
+42 −19
Original line number Diff line number Diff line
@@ -62,21 +62,25 @@ public class RuntimeInit {
    }

    /**
     * Use this to log a message when a thread exits due to an uncaught
     * exception.  The framework catches these for the main threads, so
     * this should only matter for threads created by applications.
     * Logs a message when a thread encounters an uncaught exception. By
     * default, {@link KillApplicationHandler} will terminate this process later,
     * but apps can override that behavior.
     */
    private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
    private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            try {
                // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
            // Don't re-enter if KillApplicationHandler has already run
            if (mCrashing) return;
                mCrashing = true;

            if (mApplicationObject == null) {
                // The "FATAL EXCEPTION" string is still used on Android even though
                // apps can set a custom UncaughtExceptionHandler that renders uncaught
                // exceptions non-fatal.
                Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
            } else {
                StringBuilder message = new StringBuilder();
                // The "FATAL EXCEPTION" string is still used on Android even though
                // apps can set a custom UncaughtExceptionHandler that renders uncaught
                // exceptions non-fatal.
                message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
                final String processName = ActivityThread.currentProcessName();
                if (processName != null) {
@@ -85,6 +89,21 @@ public class RuntimeInit {
                message.append("PID: ").append(Process.myPid());
                Clog_e(TAG, message.toString(), e);
            }
        }
    }

    /**
     * Handle application death from an uncaught exception.  The framework
     * catches these for the main threads, so this should only matter for
     * threads created by applications.  Before this method runs,
     * {@link LoggingHandler} will already have logged details.
     */
    private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
        public void uncaughtException(Thread t, Throwable e) {
            try {
                // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
                if (mCrashing) return;
                mCrashing = true;

                // Try to end profiling. If a profiler is running at this point, and we kill the
                // process (below), the in-memory buffer will be lost. So try to stop, which will
@@ -113,8 +132,12 @@ public class RuntimeInit {
    private static final void commonInit() {
        if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");

        /* set default handler; this applies to all threads in the VM */
        Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
        /*
         * set handlers; these apply to all threads in the VM. Apps can replace
         * the default handler, but not the pre handler.
         */
        Thread.setUncaughtExceptionPreHandler(new LoggingHandler());
        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());

        /*
         * Install a TimezoneGetter subclass for ZoneInfo.db
+2 −1
Original line number Diff line number Diff line
@@ -2257,7 +2257,8 @@ com.android.internal.os.LoggingPrintStream$1
com.android.internal.os.RuntimeInit
com.android.internal.os.RuntimeInit$1
com.android.internal.os.RuntimeInit$Arguments
com.android.internal.os.RuntimeInit$UncaughtHandler
com.android.internal.os.RuntimeInit$KillApplicationHandler
com.android.internal.os.RuntimeInit$LoggingHandler
com.android.internal.os.SamplingProfilerIntegration
com.android.internal.os.SomeArgs
com.android.internal.os.Zygote