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

Commit 20a083dc 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.

Follow ups:
1. Enable the change by default with the device config flag
2. Refactor PROC_START_TIMEOUT as an ANR

Test: atest AsyncProcessStartTest
Bug: 253908737
Change-Id: Iaa51d310e262d1cf381b7a11a65b650c735448a3
parent 16a472fa
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -505,6 +505,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;

@@ -6973,6 +6974,12 @@ public final class ActivityThread extends ClientTransactionHandler
                throw e.rethrowFromSystemServer();
            }
        }

        try {
            mgr.finishAttachApplication(mStartSeq);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

    @UnsupportedAppUsage
@@ -7777,6 +7784,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,
+27 −0
Original line number Diff line number Diff line
@@ -970,6 +970,20 @@ final class ActivityManagerConstants extends ContentObserver {
    public volatile long mShortFgsProcStateExtraWaitDuration =
            DEFAULT_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION;

    /**
     * If enabled, when starting an application, the system will wait for a
     * {@link ActivityManagerService#finishAttachApplication} from the app before scheduling
     * Broadcasts or Services to it.
     */
    private static final String KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION =
            "enable_wait_for_finish_attach_application";

    private static final boolean DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = false;

    /** @see #KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION */
    public volatile boolean mEnableWaitForFinishAttachApplication =
            DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION;

    /**
     * If a "short service" doesn't finish within this after the timeout (
     * {@link #KEY_SHORT_FGS_TIMEOUT_DURATION}), then we'll declare an ANR.
@@ -1144,6 +1158,10 @@ final class ActivityManagerConstants extends ContentObserver {
                                break;
                            case KEY_TOP_TO_FGS_GRACE_DURATION:
                                updateTopToFgsGraceDuration();
                                break;
                            case KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION:
                                updateEnableWaitForFinishAttachApplication();
                                break;
                            default:
                                break;
                        }
@@ -1866,6 +1884,13 @@ final class ActivityManagerConstants extends ContentObserver {
                DEFAULT_SHORT_FGS_ANR_EXTRA_WAIT_DURATION);
    }

    private void updateEnableWaitForFinishAttachApplication() {
        mEnableWaitForFinishAttachApplication = DeviceConfig.getBoolean(
                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION,
                DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION);
    }

    @NeverCompile // Avoid size overhead of debugging code.
    void dump(PrintWriter pw) {
        pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
@@ -2055,5 +2080,7 @@ final class ActivityManagerConstants extends ContentObserver {
        pw.print("  CUR_TRIM_EMPTY_PROCESSES="); pw.println(CUR_TRIM_EMPTY_PROCESSES);
        pw.print("  CUR_TRIM_CACHED_PROCESSES="); pw.println(CUR_TRIM_CACHED_PROCESSES);
        pw.print("  OOMADJ_UPDATE_QUICK="); pw.println(OOMADJ_UPDATE_QUICK);
        pw.print("  ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION=");
        pw.println(mEnableWaitForFinishAttachApplication);
    }
}
+184 −124
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@ import static android.os.Process.ZYGOTE_PROCESS;
import static android.os.Process.getTotalMemory;
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;
@@ -968,13 +969,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;
@@ -1006,7 +1000,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);
@@ -1017,26 +1011,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;
    }
@@ -2338,7 +2312,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);
@@ -2353,7 +2327,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);
@@ -3300,8 +3274,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        ProcessRecord record = mProcessList.getLRURecordForAppLOSP(threadBinder);
        if (record != null) return record;
        // Validation: if it isn't in the LRU list, it shouldn't exist, but let's
        // double-check that.
        // Validation: if it isn't in the LRU list, it shouldn't exist, but let's double-check that.
        final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
                mProcessList.getProcessNamesLOSP().getMap();
        for (int i = pmap.size()-1; i >= 0; i--) {
@@ -3310,8 +3283,10 @@ public class ActivityManagerService extends IActivityManager.Stub
                final ProcessRecord proc = procs.valueAt(j);
                final IApplicationThread procThread = proc.getThread();
                if (procThread != null && procThread.asBinder() == threadBinder) {
                    if (!proc.isPendingFinishAttach()) {
                        Slog.wtf(TAG, "getRecordForApp: exists in name list but not in LRU list: "
                                + proc);
                    }
                    return proc;
                }
            }
@@ -4742,7 +4717,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) {
@@ -4823,7 +4798,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
@@ -4888,7 +4863,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                    // Ignore exceptions.
                }
            }
            return false;
            return;
        }
        // If this application record is still attached to a previous
@@ -4913,7 +4888,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);
@@ -4936,8 +4911,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)
@@ -5088,6 +5061,8 @@ public class ActivityManagerService extends IActivityManager.Stub
                profilerInfo = null;
            }
            app.setBindApplicationTime(bindApplicationTimeMillis);
            // Make app active after binding application or client may be running requests (e.g
            // starting activities) before it is ready.
            synchronized (mProcLock) {
@@ -5101,6 +5076,19 @@ public class ActivityManagerService extends IActivityManager.Stub
                app.mProfile.setLastRequestedGc(now);
                app.mProfile.setLastLowMemory(now);
            }
            // Remove this record from the list of starting applications.
            mPersistentStartingProcesses.remove(app);
            if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) {
                Slog.v(TAG_PROCESSES, "Attach application locked removing on hold: " + app);
            }
            mProcessesOnHold.remove(app);
            if (!mConstants.mEnableWaitForFinishAttachApplication) {
                finishAttachApplicationInner(startSeq, callingUid, pid);
            } else {
                app.setPendingFinishAttach(true);
            }
        } catch (Exception e) {
            // We need kill the process group here. (b/148588589)
            Slog.wtf(TAG, "Exception thrown during bind of " + app, e);
@@ -5109,15 +5097,58 @@ 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.
        mPersistentStartingProcesses.remove(app);
        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");
            synchronized (this) {
                app.killLocked("unexpected process record",
                        ApplicationExitInfo.REASON_OTHER, true);
            }
            return;
        }
        synchronized (this) {
            // Mark the finish attach application phase as completed
            app.setPendingFinishAttach(false);
            final boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
            final String processName = app.processName;
            boolean badApp = false;
            boolean didSomething = false;
@@ -5135,7 +5166,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;
@@ -5148,7 +5180,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);
@@ -5157,13 +5190,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) {
@@ -5173,20 +5210,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,
@@ -5194,26 +5230,35 @@ 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 (!mConstants.mEnableWaitForFinishAttachApplication) {
            Slog.i(TAG, "Flag disabled. Ignoring finishAttachApplication from uid: "
                    + uid + ". pid: " + pid);
            return;
        }
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
        if (pid == MY_PID && uid == SYSTEM_UID) {
            return;
        }
        final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
        try {
            finishAttachApplicationInner(startSeq, uid, pid);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
@@ -19011,6 +19056,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
@@ -1228,7 +1228,7 @@ public class OomAdjuster {
        for (int i = numLru - 1; i >= 0; i--) {
            ProcessRecord app = lruList.get(i);
            final ProcessStateRecord state = app.mState;
            if (!app.isKilledByAm() && app.getThread() != null) {
            if (!app.isKilledByAm() && app.getThread() != null && !app.isPendingFinishAttach()) {
                // We don't need to apply the update for the process which didn't get computed
                if (state.getCompletedAdjSeq() == mAdjSeq) {
                    applyOomAdjLSP(app, true, now, nowElapsed, oomAdjReason);
Loading