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

Commit 331ef23f authored by Zim's avatar Zim
Browse files

Split attachApplication to more accurately identify app startup

Previously, while handling the attachApplication from an app at
startup, the system started the broadcast (and service) AnR timeout
countdown as soon as it sent async bindApplication request to the app.

This increased the likelyhood of AnRs under CPU contention since some
fixed process initialization costs were included in the broadcast handling
delay.

Now, we split the the attachApplication into 2 phases, there's now a new
finishAttachApplication which apps call right before they start executing any
custom code. This new call unblocks any pending broadcast/service scheduling
and starts the appropriate AnR timeout countdowns.

Test: atest AsyncProcessStartTest
Bug: 253908737
Change-Id: I61046201281acf6686290d0ab83174ab134d5ef2
parent 28b62162
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -502,6 +502,7 @@ public final class ActivityThread extends ClientTransactionHandler

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    static volatile Handler sMainThreadHandler;  // set once in main()
    private long mStartSeq; // Only accesssed from the main thread

    Bundle mCoreSettings = null;

@@ -6809,6 +6810,14 @@ public final class ActivityThread extends ClientTransactionHandler
        Application app;
        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
        final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();

        final IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.finishAttachApplication(mStartSeq);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }

        try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
@@ -7649,6 +7658,8 @@ public final class ActivityThread extends ClientTransactionHandler
        sCurrentActivityThread = this;
        mConfigurationController = new ConfigurationController(this);
        mSystemThread = system;
        mStartSeq = startSeq;

        if (!system) {
            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                    UserHandle.myUserId());
+3 −2
Original line number Diff line number Diff line
@@ -147,6 +147,7 @@ interface IActivityManager {
    oneway void finishReceiver(in IBinder who, int resultCode, in String resultData, in Bundle map,
            boolean abortBroadcast, int flags);
    void attachApplication(in IApplicationThread app, long startSeq);
    void finishAttachApplication(long startSeq);
    List<ActivityManager.RunningTaskInfo> getTasks(int maxNum);
    @UnsupportedAppUsage
    void moveTaskToFront(in IApplicationThread caller, in String callingPackage, int task,
+155 −116
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ import static android.os.Process.getTotalMemory;
import static android.os.Process.isSdkSandboxUid;
import static android.os.Process.isThreadInProcess;
import static android.os.Process.killProcess;
import static android.os.Process.killProcessGroup;
import static android.os.Process.killProcessQuiet;
import static android.os.Process.myPid;
import static android.os.Process.myUid;
@@ -952,13 +953,6 @@ public class ActivityManagerService extends IActivityManager.Stub
            }
            return false;
        }
        boolean doRemoveIfNoThreadInternal(int pid, ProcessRecord app) {
            if (app == null || app.getThread() != null) {
                return false;
            }
            return doRemoveInternal(pid, app);
        }
    }
    private final PendingStartActivityUids mPendingStartActivityUids;
@@ -990,7 +984,7 @@ public class ActivityManagerService extends IActivityManager.Stub
     * method.
     */
    @GuardedBy("this")
    void removePidLocked(int pid, ProcessRecord app) {
    boolean removePidLocked(int pid, ProcessRecord app) {
        final boolean removed;
        synchronized (mPidsSelfLocked) {
            removed = mPidsSelfLocked.doRemoveInternal(pid, app);
@@ -1001,26 +995,6 @@ public class ActivityManagerService extends IActivityManager.Stub
            }
            mAtmInternal.onProcessUnMapped(pid);
        }
    }
    /**
     * Removes the process record from the map if it doesn't have a thread.
     * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
     * method.
     */
    @GuardedBy("this")
    private boolean removePidIfNoThreadLocked(ProcessRecord app) {
        final boolean removed;
        final int pid = app.getPid();
        synchronized (mPidsSelfLocked) {
            removed = mPidsSelfLocked.doRemoveIfNoThreadInternal(pid, app);
        }
        if (removed) {
            synchronized (sActiveProcessInfoSelfLocked) {
                sActiveProcessInfoSelfLocked.remove(pid);
            }
            mAtmInternal.onProcessUnMapped(pid);
        }
        return removed;
    }
@@ -2364,7 +2338,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        mAppErrors = null;
        mPackageWatchdog = null;
        mAppOpsService = mInjector.getAppOpsService(null /* file */, null /* handler */);
        mBatteryStatsService = null;
        mBatteryStatsService = mInjector.getBatteryStatsService();
        mHandler = new MainHandler(handlerThread.getLooper());
        mHandlerThread = handlerThread;
        mConstants = new ActivityManagerConstants(mContext, this, mHandler);
@@ -2379,7 +2353,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        mIntentFirewall = null;
        mProcessStats = new ProcessStatsService(this, mContext.getCacheDir());
        mCpHelper = new ContentProviderHelper(this, false);
        mServices = null;
        mServices = mInjector.getActiveServices(this);
        mSystemThread = null;
        mUiHandler = injector.getUiHandler(null /* service */);
        mUidObserverController = new UidObserverController(mUiHandler);
@@ -4771,7 +4745,7 @@ public class ActivityManagerService extends IActivityManager.Stub
    @GuardedBy("this")
    void handleProcessStartOrKillTimeoutLocked(ProcessRecord app, boolean isKillTimeout) {
        final int pid = app.getPid();
        boolean gone = isKillTimeout || removePidIfNoThreadLocked(app);
        boolean gone = isKillTimeout || removePidLocked(pid, app);
        if (gone) {
            if (isKillTimeout) {
@@ -4852,7 +4826,7 @@ public class ActivityManagerService extends IActivityManager.Stub
    }
    @GuardedBy("this")
    private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
    private void attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
        // Find the application record that is being attached...  either via
@@ -4917,7 +4891,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                    // Ignore exceptions.
                }
            }
            return false;
            return;
        }
        // If this application record is still attached to a previous
@@ -4942,7 +4916,7 @@ public class ActivityManagerService extends IActivityManager.Stub
            mProcessList.startProcessLocked(app,
                    new HostingRecord(HostingRecord.HOSTING_TYPE_LINK_FAIL, processName),
                    ZYGOTE_POLICY_FLAG_EMPTY);
            return false;
            return;
        }
        EventLogTags.writeAmProcBound(app.userId, pid, app.processName);
@@ -4965,8 +4939,6 @@ public class ActivityManagerService extends IActivityManager.Stub
            app.setUnlocked(StorageManager.isUserKeyUnlocked(app.userId));
        }
        mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
        boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
        List<ProviderInfo> providers = normalMode
                                            ? mCpHelper.generateApplicationProvidersLocked(app)
@@ -5132,7 +5104,7 @@ public class ActivityManagerService extends IActivityManager.Stub
            app.killLocked("error during bind", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
                    true);
            handleAppDiedLocked(app, pid, false, true, false /* fromBinderDied */);
            return false;
            return;
        }
        // Remove this record from the list of starting applications.
@@ -5140,7 +5112,51 @@ public class ActivityManagerService extends IActivityManager.Stub
        if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
                "Attach application locked removing on hold: " + app);
        mProcessesOnHold.remove(app);
    }
    @Override
    public final void attachApplication(IApplicationThread thread, long startSeq) {
        if (thread == null) {
            throw new SecurityException("Invalid application interface");
        }
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            Binder.restoreCallingIdentity(origId);
        }
    }
    private void finishAttachApplicationInner(long startSeq, int uid, int pid) {
        final long startTime = SystemClock.uptimeMillis();
        // Find the application record that is being attached...  either via
        // the pid if we are running in multiple processes, or just pull the
        // next app record if we are emulating process with anonymous threads.
        final ProcessRecord app;
        synchronized (mPidsSelfLocked) {
            app = mPidsSelfLocked.get(pid);
        }
        if (app != null && app.getStartUid() == uid && app.getStartSeq() == startSeq) {
            mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
        } else {
            Slog.wtf(TAG, "Mismatched or missing ProcessRecord: " + app + ". Pid: " + pid
                    + ". Uid: " + uid);
            killProcess(pid);
            killProcessGroup(uid, pid);
            mProcessList.noteAppKill(pid, uid,
                    ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
                    ApplicationExitInfo.SUBREASON_UNKNOWN,
                    "wrong startSeq");
            app.killLocked("unexpected process record",
                    ApplicationExitInfo.REASON_OTHER, true);
            return;
        }
        synchronized (this) {
            final boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
            final String processName = app.processName;
            boolean badApp = false;
            boolean didSomething = false;
@@ -5158,7 +5174,8 @@ public class ActivityManagerService extends IActivityManager.Stub
            if (!badApp) {
                try {
                    didSomething |= mServices.attachApplicationLocked(app, processName);
                checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");
                    checkTime(startTime, "finishAttachApplicationInner: "
                            + "after mServices.attachApplicationLocked");
                } catch (Exception e) {
                    Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
                    badApp = true;
@@ -5171,7 +5188,8 @@ public class ActivityManagerService extends IActivityManager.Stub
                    for (BroadcastQueue queue : mBroadcastQueues) {
                        didSomething |= queue.onApplicationAttachedLocked(app);
                    }
                checkTime(startTime, "attachApplicationLocked: after dispatching broadcasts");
                    checkTime(startTime, "finishAttachApplicationInner: "
                            + "after dispatching broadcasts");
                } catch (Exception e) {
                    // If the app died trying to launch the receiver we declare it 'bad'
                    Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
@@ -5180,13 +5198,17 @@ public class ActivityManagerService extends IActivityManager.Stub
            }
            // Check whether the next backup agent is in this process...
            final BackupRecord backupTarget = mBackupTargets.get(app.userId);
            if (!badApp && backupTarget != null && backupTarget.app == app) {
            if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
                if (DEBUG_BACKUP) {
                    Slog.v(TAG_BACKUP,
                            "New app is backup target, launching agent for " + app);
                }
                notifyPackageUse(backupTarget.appInfo.packageName,
                        PackageManager.NOTIFY_PACKAGE_USE_BACKUP);
                try {
                thread.scheduleCreateBackupAgent(backupTarget.appInfo,
                    app.getThread().scheduleCreateBackupAgent(backupTarget.appInfo,
                            backupTarget.backupMode, backupTarget.userId,
                            backupTarget.backupDestination);
                } catch (Exception e) {
@@ -5196,20 +5218,19 @@ public class ActivityManagerService extends IActivityManager.Stub
            }
            if (badApp) {
            app.killLocked("error during init", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
                    true);
                app.killLocked("error during init",
                        ApplicationExitInfo.REASON_INITIALIZATION_FAILURE, true);
                handleAppDiedLocked(app, pid, false, true, false /* fromBinderDied */);
            return false;
                return;
            }
            if (!didSomething) {
                updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
            checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
                checkTime(startTime, "finishAttachApplicationInner: after updateOomAdjLocked");
            }
            final HostingRecord hostingRecord = app.getHostingRecord();
        String shortAction = getShortAction(hostingRecord.getAction());
            final String shortAction = getShortAction(hostingRecord.getAction());
            FrameworkStatsLog.write(
                    FrameworkStatsLog.PROCESS_START_TIME,
                    app.info.uid,
@@ -5217,26 +5238,29 @@ public class ActivityManagerService extends IActivityManager.Stub
                    app.info.packageName,
                    FrameworkStatsLog.PROCESS_START_TIME__TYPE__COLD,
                    app.getStartElapsedTime(),
                (int) (bindApplicationTimeMillis - app.getStartUptime()),
                    (int) (app.getBindApplicationTime() - app.getStartUptime()),
                    (int) (SystemClock.uptimeMillis() - app.getStartUptime()),
                    hostingRecord.getType(),
                    hostingRecord.getName(),
                    shortAction,
                    HostingRecord.getHostingTypeIdStatsd(hostingRecord.getType()),
                    HostingRecord.getTriggerTypeForStatsd(hostingRecord.getTriggerType()));
        return true;
        }
    }
    @Override
    public final void attachApplication(IApplicationThread thread, long startSeq) {
        if (thread == null) {
            throw new SecurityException("Invalid application interface");
    public final void finishAttachApplication(long startSeq) {
        final int pid = Binder.getCallingPid();
        final int uid = Binder.getCallingUid();
        if (pid == MY_PID && uid == SYSTEM_UID) {
            return;
        }
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
        try {
            finishAttachApplicationInner(startSeq, uid, pid);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
@@ -18805,6 +18829,21 @@ public class ActivityManagerService extends IActivityManager.Stub
            return new ProcessList();
        }
        /**
         * Returns the {@link BatteryStatsService} instance
         */
        public BatteryStatsService getBatteryStatsService() {
            return new BatteryStatsService(mContext, SystemServiceManager.ensureSystemDir(),
                BackgroundThread.get().getHandler());
        }
        /**
         * Returns the {@link ActiveServices} instance
         */
        public ActiveServices getActiveServices(ActivityManagerService service) {
            return new ActiveServices(service);
        }
        private boolean ensureHasNetworkManagementInternal() {
            if (mNmi == null) {
                mNmi = LocalServices.getService(NetworkManagementInternal.class);
+1 −1
Original line number Diff line number Diff line
@@ -2508,7 +2508,7 @@ public final class ProcessList {
    }

    @GuardedBy("mService")
    private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
    String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
        StringBuilder sb = null;
        if (app.isKilledByAm()) {
            if (sb == null) sb = new StringBuilder();
+9 −0
Original line number Diff line number Diff line
@@ -199,6 +199,11 @@ class ProcessRecord implements WindowProcessListener {
     */
    private volatile long mStartElapsedTime;

    /**
     * When the process was sent the bindApplication request
     */
    private volatile long mBindApplicationTime;

    /**
     * This will be same as {@link #uid} usually except for some apps used during factory testing.
     */
@@ -739,6 +744,10 @@ class ProcessRecord implements WindowProcessListener {
        return mStartElapsedTime;
    }

    long getBindApplicationTime() {
        return mBindApplicationTime;
    }

    int getStartUid() {
        return mStartUid;
    }
Loading