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

Commit 196db719 authored by Wale Ogunwale's avatar Wale Ogunwale
Browse files

Move activity timeouts to ActivityRecord (81/n)

Reduces reliance on ActivityStack
Also:
- Removed AS#mWindowManager and use mWmService instead
- Renames AS#mService to mAtmService
- Removed AS#mLruActivities which was only used for logging

Change-Id: I61ade9104b9be65b69f68f3055ad2c226d364202
Test: Existing tests pass.
Bug: 80414790
parent 88233e4c
Loading
Loading
Loading
Loading
+153 −21
Original line number Diff line number Diff line
@@ -379,6 +379,27 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    @VisibleForTesting static final int Z_BOOST_BASE = 800570000;
    static final int INVALID_PID = -1;

    // How long we wait until giving up on the last activity to pause.  This
    // is short because it directly impacts the responsiveness of starting the
    // next activity.
    private static final int PAUSE_TIMEOUT = 500;

    // Ticks during which we check progress while waiting for an app to launch.
    private static final int LAUNCH_TICK = 500;

    // How long we wait for the activity to tell us it has stopped before
    // giving up.  This is a good amount of time because we really need this
    // from the application in order to get its saved state. Once the stop
    // is complete we may start destroying client resources triggering
    // crashes if the UI thread was hung. We put this timeout one second behind
    // the ANR timeout so these situations will generate ANR instead of
    // Surface lost or other errors.
    private static final int STOP_TIMEOUT = 11 * 1000;

    // How long we wait until giving up on an activity telling us it has
    // finished destroying itself.
    private static final int DESTROY_TIMEOUT = 10 * 1000;

    final ActivityTaskManagerService mAtmService;
    final ActivityInfo info; // activity info provided by developer in AndroidManifest
    // Non-null only for application tokens.
@@ -681,6 +702,55 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    // Token for targeting this activity for assist purposes.
    final Binder assistToken = new Binder();

    private final Runnable mPauseTimeoutRunnable = new Runnable() {
        @Override
        public void run() {
            // We don't at this point know if the activity is fullscreen,
            // so we need to be conservative and assume it isn't.
            Slog.w(TAG, "Activity pause timeout for " + ActivityRecord.this);
            synchronized (mAtmService.mGlobalLock) {
                if (hasProcess()) {
                    mAtmService.logAppTooSlow(app, pauseTime, "pausing " + ActivityRecord.this);
                }
                activityPaused(true);
            }
        }
    };

    private final Runnable mLaunchTickRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (mAtmService.mGlobalLock) {
                if (continueLaunchTicking()) {
                    mAtmService.logAppTooSlow(
                            app, launchTickTime, "launching " + ActivityRecord.this);
                }
            }
        }
    };

    private final Runnable mDestroyTimeoutRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (mAtmService.mGlobalLock) {
                Slog.w(TAG, "Activity destroy timeout for " + ActivityRecord.this);
                destroyed("destroyTimeout");
            }
        }
    };

    private final Runnable mStopTimeoutRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (mAtmService.mGlobalLock) {
                Slog.w(TAG, "Activity stop timeout for " + ActivityRecord.this);
                if (isInHistory()) {
                    activityStopped(null /*icicle*/, null /*persistentState*/, null /*description*/);
                }
            }
        }
    };

    private static String startingWindowStateToString(int state) {
        switch (state) {
            case STARTING_WINDOW_NOT_SHOWN:
@@ -2684,11 +2754,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        EventLogTags.writeWmDestroyActivity(mUserId, System.identityHashCode(this),
                task.mTaskId, shortComponentName, reason);

        final ActivityStack stack = getActivityStack();
        if (hasProcess() && !stack.inLruList(this)) {
            Slog.w(TAG, "Activity " + this + " being finished, but not in LRU list");
        }

        boolean removedFromHistory = false;

        cleanUp(false /* cleanServices */, false /* setState */);
@@ -2735,7 +2800,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                }
                setState(DESTROYING,
                        "destroyActivityLocked. finishing and not skipping destroy");
                stack.scheduleDestroyTimeoutForActivity(this);
                mAtmService.mH.postDelayed(mDestroyTimeoutRunnable, DESTROY_TIMEOUT);
            } else {
                if (DEBUG_STATES) {
                    Slog.v(TAG_STATES, "Moving to DESTROYED: " + this + " (destroy skipped)");
@@ -2785,8 +2850,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }

        takeFromHistory();
        final ActivityStack stack = getActivityStack();
        stack.removeTimeoutsForActivity(this);
        removeTimeouts();
        if (DEBUG_STATES) {
            Slog.v(TAG_STATES, "Moving to DESTROYED: " + this + " (removed from history)");
        }
@@ -2814,7 +2878,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
     * AND finished.
     */
    void destroyed(String reason) {
        getActivityStack().removeDestroyTimeoutForActivity(this);
        removeDestroyTimeout();

        if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + this);

@@ -2872,7 +2936,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }

        // Get rid of any pending idle timeouts.
        stack.removeTimeoutsForActivity(this);
        removeTimeouts();
        // Clean-up activities are no longer relaunching (e.g. app process died). Notify window
        // manager so it can update its bookkeeping.
        clearRelaunching();
@@ -4737,6 +4801,73 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }
    }

    void activityPaused(boolean timeout) {
        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,
                "Activity paused: token=" + appToken + ", timeout=" + timeout);

        final ActivityStack stack = getStack();

        if (stack != null) {
            removePauseTimeout();

            if (stack.mPausingActivity == this) {
                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + this
                        + (timeout ? " (due to timeout)" : " (pause complete)"));
                mAtmService.deferWindowLayout();
                try {
                    stack.completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
                } finally {
                    mAtmService.continueWindowLayout();
                }
                return;
            } else {
                EventLogTags.writeWmFailedToPause(mUserId, System.identityHashCode(this),
                        shortComponentName, stack.mPausingActivity != null
                                ? stack.mPausingActivity.shortComponentName : "(none)");
                if (isState(PAUSING)) {
                    setState(PAUSED, "activityPausedLocked");
                    if (finishing) {
                        if (DEBUG_PAUSE) Slog.v(TAG,
                                "Executing finish of failed to pause activity: " + this);
                        completeFinishing("activityPausedLocked");
                    }
                }
            }
        }

        mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
    }

    /**
     * Schedule a pause timeout in case the app doesn't respond. We don't give it much time because
     * this directly impacts the responsiveness seen by the user.
     */
    void schedulePauseTimeout() {
        pauseTime = SystemClock.uptimeMillis();
        mAtmService.mH.postDelayed(mPauseTimeoutRunnable, PAUSE_TIMEOUT);
        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
    }

    private void removePauseTimeout() {
        mAtmService.mH.removeCallbacks(mPauseTimeoutRunnable);
    }

    private void removeDestroyTimeout() {
        mAtmService.mH.removeCallbacks(mDestroyTimeoutRunnable);
    }

    private void removeStopTimeout() {
        mAtmService.mH.removeCallbacks(mStopTimeoutRunnable);
    }

    void removeTimeouts() {
        mStackSupervisor.removeIdleTimeoutForActivity(this);
        removePauseTimeout();
        removeStopTimeout();
        removeDestroyTimeout();
        finishLaunchTickingLocked();
    }

    void stopIfPossible() {
        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + this);
        final ActivityStack stack = getActivityStack();
@@ -4783,7 +4914,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            if (stack.shouldSleepOrShutDownActivities()) {
                setSleeping(true);
            }
            stack.scheduleStopTimeoutForActivity(this);
            mAtmService.mH.postDelayed(mStopTimeoutRunnable, STOP_TIMEOUT);
        } catch (Exception e) {
            // Maybe just ignore exceptions here...  if the process has crashed, our death
            // notification will clean things up.
@@ -4798,13 +4929,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }
    }

    final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
    void activityStopped(Bundle newIcicle, PersistableBundle newPersistentState,
            CharSequence description) {
        final ActivityStack stack = getActivityStack();
        final boolean isStopping = mState == STOPPING;
        if (!isStopping && mState != RESTARTING_PROCESS) {
            Slog.i(TAG, "Activity reported stop, but no longer stopping: " + this);
            stack.removeStopTimeoutForActivity(this);
            removeStopTimeout();
            return;
        }
        if (newPersistentState != null) {
@@ -4822,7 +4953,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + mIcicle);
        if (!stopped) {
            if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPED: " + this + " (stop complete)");
            stack.removeStopTimeoutForActivity(this);
            removeStopTimeout();
            stopped = true;
            if (isStopping) {
                setState(STOPPED, "activityStoppedLocked");
@@ -4877,11 +5008,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }
        if (launchTickTime == 0) {
            launchTickTime = SystemClock.uptimeMillis();
            continueLaunchTickingLocked();
            continueLaunchTicking();
        }
    }

    boolean continueLaunchTickingLocked() {
    private boolean continueLaunchTicking() {
        if (launchTickTime == 0) {
            return false;
        }
@@ -4892,10 +5023,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }

        stack.removeLaunchTickMessages();
        stack.scheduleLaunchTickForActivity(this);
        mAtmService.mH.postDelayed(mLaunchTickRunnable, LAUNCH_TICK);
        return true;
    }

    void removeLaunchTickRunnable() {
        mAtmService.mH.removeCallbacks(mLaunchTickRunnable);
    }

    void finishLaunchTickingLocked() {
        launchTickTime = 0;
        final ActivityStack stack = getActivityStack();
@@ -7017,10 +7152,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            newIntents = null;
            mAtmService.getAppWarningsLocked().onResumeActivity(this);
        } else {
            final ActivityStack stack = getActivityStack();
            if (stack != null) {
                stack.removePauseTimeoutForActivity(this);
            }
            removePauseTimeout();
            setState(PAUSED, "relaunchActivityLocked");
        }

+61 −317

File changed.

Preview size limit exceeded, changes collapsed.

+27 −5
Original line number Diff line number Diff line
@@ -52,7 +52,9 @@ import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;

import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
import static com.android.server.wm.ActivityStack.TAG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
@@ -956,9 +958,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
        }

        r.launchFailed = false;
        if (stack.updateLruList(r)) {
            Slog.w(TAG, "Activity " + r + " being launched, but already in LRU list");
        }

        // TODO(lifecycler): Resume or pause requests are done as part of launch transaction,
        // so updating the state should be done accordingly.
@@ -2162,7 +2161,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
                    if (!processPausingActivities && s.isState(PAUSING)) {
                        // Defer processing pausing activities in this iteration and reschedule
                        // a delayed idle to reprocess it again
                        removeTimeoutsForActivityLocked(idleActivity);
                        removeIdleTimeoutForActivity(idleActivity);
                        scheduleIdleTimeoutLocked(idleActivity);
                        continue;
                    }
@@ -2181,6 +2180,29 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
        return stops;
    }

    void removeHistoryRecords(WindowProcessController app) {
        removeHistoryRecords(mStoppingActivities, app, "mStoppingActivities");
        removeHistoryRecords(mGoingToSleepActivities, app, "mGoingToSleepActivities");
        removeHistoryRecords(mFinishingActivities, app, "mFinishingActivities");
    }

    private void removeHistoryRecords(ArrayList<ActivityRecord> list, WindowProcessController app,
            String listName) {
        int i = list.size();
        if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                "Removing app " + this + " from list " + listName + " with " + i + " entries");
        while (i > 0) {
            i--;
            ActivityRecord r = list.get(i);
            if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "Record #" + i + " " + r);
            if (r.app == app) {
                if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "---> REMOVING this entry!");
                list.remove(i);
                r.removeTimeouts();
            }
        }
    }

    public void dump(PrintWriter pw, String prefix) {
        pw.println();
        pw.println("ActivityStackSupervisor state:");
@@ -2371,7 +2393,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
        scheduleTopResumedActivityStateIfNeeded();
    }

    void removeTimeoutsForActivityLocked(ActivityRecord r) {
    void removeIdleTimeoutForActivity(ActivityRecord r) {
        if (DEBUG_IDLE) Slog.d(TAG_IDLE, "removeTimeoutsForActivity: Callers="
                + Debug.getCallers(4));
        mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+6 −6
Original line number Diff line number Diff line
@@ -306,7 +306,7 @@ import java.util.Set;
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
    private static final String TAG_STACK = TAG + POSTFIX_STACK;
    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
    static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
    private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
    private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
@@ -1730,9 +1730,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        final long origId = Binder.clearCallingIdentity();
        synchronized (mGlobalLock) {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused");
            ActivityStack stack = ActivityRecord.getStackLocked(token);
            if (stack != null) {
                stack.activityPausedLocked(token, false);
            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
            if (r != null) {
                r.activityPaused(false);
            }
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
@@ -1765,7 +1765,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
                    restartingName = r.app.mName;
                    restartingUid = r.app.mUid;
                }
                r.activityStoppedLocked(icicle, persistentState, description);
                r.activityStopped(icicle, persistentState, description);
            }
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
@@ -7304,7 +7304,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        @Override
        public void scheduleDestroyAllActivities(String reason) {
            synchronized (mGlobalLock) {
                mRootActivityContainer.scheduleDestroyAllActivities(null, reason);
                mRootActivityContainer.scheduleDestroyAllActivities(reason);
            }
        }

+38 −9
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
@@ -62,6 +63,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;

@@ -205,6 +207,28 @@ class RootActivityContainer extends RootWindowContainer
    private boolean mTmpBoolean;
    private RemoteException mTmpRemoteException;

    private String mDestroyAllActivitiesReason;
    private final Runnable mDestroyAllActivitiesRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (mService.mGlobalLock) {
                try {
                    mStackSupervisor.beginDeferResume();

                    final PooledConsumer c = PooledLambda.obtainConsumer(
                            RootActivityContainer::destroyActivity, RootActivityContainer.this,
                            PooledLambda.__(ActivityRecord.class));
                    forAllActivities(c);
                    c.recycle();
                } finally {
                    mStackSupervisor.endDeferResume();
                    resumeFocusedStacksTopActivities();
                }
            }
        }

    };

    private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
    static class FindTaskResult implements Function<Task, Boolean> {
        ActivityRecord mRecord;
@@ -1653,14 +1677,19 @@ class RootActivityContainer extends RootWindowContainer
        }
    }

    void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
        for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
            final DisplayContent display = getChildAt(displayNdx);
            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = display.getStackAt(stackNdx);
                stack.scheduleDestroyActivities(app, reason);
            }
    void scheduleDestroyAllActivities(String reason) {
        mDestroyAllActivitiesReason = reason;
        mService.mH.post(mDestroyAllActivitiesRunnable);
    }

    private void destroyActivity(ActivityRecord r) {
        if (r.finishing || !r.isDestroyable()) return;

        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Destroying " + r + " in state " + r.getState()
                + " resumed=" + r.getStack().mResumedActivity + " pausing="
                + r.getStack().mPausingActivity + " for reason " + mDestroyAllActivitiesReason);

        r.destroyImmediately(true /* removeFromTask */, mDestroyAllActivitiesReason);
    }

    // Tries to put all activity stacks to sleep. Returns true if all stacks were
@@ -2059,7 +2088,7 @@ class RootActivityContainer extends RootWindowContainer
            final DisplayContent display = getChildAt(displayNdx);
            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = display.getStackAt(stackNdx);
                hasVisibleActivities |= stack.handleAppDiedLocked(app);
                hasVisibleActivities |= stack.handleAppDied(app);
            }
        }
        return hasVisibleActivities;
Loading