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

Commit 18005c14 authored by Makoto Onuki's avatar Makoto Onuki
Browse files

Add a check to detect "duplicate app instances" bug

Test: Boot
Test: Added debug code to induce the WTF situation, booted and checked the log
Bug: 185177290

Change-Id: Ie95164f2794b461115f07d02c2363fbdc9a517b4
parent 056333fa
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
@@ -29,9 +29,13 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.view.autofill.AutofillManager;

import com.android.internal.annotations.GuardedBy;

import java.util.ArrayList;

/**
@@ -53,6 +57,10 @@ import java.util.ArrayList;
 */
public class Application extends ContextWrapper implements ComponentCallbacks2 {
    private static final String TAG = "Application";

    /** Whether to enable the check to detect "duplicate application instances". */
    private static final boolean DEBUG_DUP_APP_INSTANCES = true;

    @UnsupportedAppUsage
    private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
            new ArrayList<ActivityLifecycleCallbacks>();
@@ -66,6 +74,13 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {
    @UnsupportedAppUsage
    public LoadedApk mLoadedApk;

    @GuardedBy("sInstances")
    private static final ArrayMap<Class<?>, Application> sInstances =
            DEBUG_DUP_APP_INSTANCES ? new ArrayMap<>(1) : null;

    // Only set when DEBUG_DUP_APP_INSTANCES is true.
    private StackTrace mConstructorStackTrace;

    public interface ActivityLifecycleCallbacks {

        /**
@@ -231,6 +246,41 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {

    public Application() {
        super(null);
        if (DEBUG_DUP_APP_INSTANCES) {
            checkDuplicateInstances();
        }
    }

    private void checkDuplicateInstances() {
        final Class<?> myClass = this.getClass();

        // We only activate this check for custom application classes.
        // Otherwise, it'd misfire if multiple apps share the same process, if all of them use
        // the same Application class (on the same classloader).
        if (myClass == Application.class) {
            return;
        }
        synchronized (sInstances) {
            final Application firstInstance = sInstances.get(myClass);
            if (firstInstance == null) {
                this.mConstructorStackTrace = new StackTrace("First ctor was called here");
                sInstances.put(myClass, this);
                return;
            }
            final StackTrace currentStackTrace = new StackTrace("Current ctor was called here",
                    firstInstance.mConstructorStackTrace);
            this.mConstructorStackTrace = currentStackTrace;
            Slog.wtf(TAG, "Application ctor called twice for " + myClass
                    + " first LoadedApk=" + firstInstance.getLoadedApkInfo(),
                    currentStackTrace);
        }
    }

    private String getLoadedApkInfo() {
        if (mLoadedApk == null) {
            return "null";
        }
        return mLoadedApk + "/pkg=" + mLoadedApk.mPackageName;
    }

    /**
+4 −0
Original line number Diff line number Diff line
@@ -24,4 +24,8 @@ public class StackTrace extends Exception {
    public StackTrace(String message) {
        super(message);
    }

    public StackTrace(String message, Throwable innerStackTrace) {
        super(message, innerStackTrace);
    }
}