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

Commit fc8ab261 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Refactor reporting activity idle

- Separate the logic of stopping and finishing from idle.
- Correct the condition of stop profiling from idle.
- Remove the resuming of focused stack if an activity is
  removed while idle, because if idle is scheduled or reported,
  the next focused activity should be ready to show, then the
  resuming from background destruction is a no-op.

Bug: 130340090
Test: atest ActivityLifecycleTests

Change-Id: Iabccc26f40c8cbccb8e4a260d8aff84322193f2b
parent fbe8eca9
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -4772,7 +4772,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }

        // Schedule an idle timeout in case the app doesn't do it for us.
        mStackSupervisor.scheduleIdleTimeoutLocked(this);
        mStackSupervisor.scheduleIdleTimeout(this);

        mStackSupervisor.reportResumedActivityLocked(this);

@@ -4993,9 +4993,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                        + "immediate=" + !idleDelayed);
            }
            if (!idleDelayed) {
                mStackSupervisor.scheduleIdleLocked();
                mStackSupervisor.scheduleIdle();
            } else {
                mStackSupervisor.scheduleIdleTimeoutLocked(this);
                mStackSupervisor.scheduleIdleTimeout(this);
            }
        } else {
            stack.checkReadyForSleep();
@@ -6111,13 +6111,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            // the full stop of this activity. This is because we won't do that while they are still
            // waiting for the animation to finish.
            if (mAtmService.mStackSupervisor.mStoppingActivities.contains(this)) {
                mAtmService.mStackSupervisor.scheduleIdleLocked();
                mStackSupervisor.scheduleIdle();
            }
        } else {
            // Instead of doing the full stop routine here, let's just hide any activities
            // we now can, and let them stop when the normal idle happens.
            mAtmService.mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */,
                    false /* remove */, true /* processPausingActivities */);
            mStackSupervisor.processStoppingActivities(null /* launchedActivity */,
                    true /* onlyUpdateVisibility */, true /* unused */, null /* unused */);
        }
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }
+2 −2
Original line number Diff line number Diff line
@@ -1271,7 +1271,7 @@ class ActivityStack extends WindowContainer<WindowContainer> implements BoundsAn
        // Make sure that there is no activity waiting for this to launch.
        if (!mStackSupervisor.mWaitingActivityLaunched.isEmpty()) {
            mStackSupervisor.removeIdleTimeoutForActivity(r);
            mStackSupervisor.scheduleIdleTimeoutLocked(r);
            mStackSupervisor.scheduleIdleTimeout(r);
        }
    }

@@ -1324,7 +1324,7 @@ class ActivityStack extends WindowContainer<WindowContainer> implements BoundsAn
                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to stop "
                        + mStackSupervisor.mStoppingActivities.size() + " activities");

                mStackSupervisor.scheduleIdleLocked();
                mStackSupervisor.scheduleIdle();
                shouldSleep = false;
            }

+156 −163
Original line number Diff line number Diff line
@@ -1297,22 +1297,14 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
        return booting;
    }

    // Checked.
    @GuardedBy("mService")
    final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
    void activityIdleInternal(ActivityRecord r, boolean fromTimeout,
            boolean processPausingActivities, Configuration config) {
        if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);
        if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + r);

        ArrayList<ActivityRecord> finishes = null;
        ArrayList<UserState> startingUsers = null;
        int NS = 0;
        int NF = 0;
        boolean booting = false;
        boolean activityRemoved = false;

        ActivityRecord r = ActivityRecord.forTokenLocked(token);
        if (r != null) {
            if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternalLocked: Callers="
            if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternal: Callers="
                    + Debug.getCallers(4));
            mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
            r.finishLaunchTickingLocked();
@@ -1365,47 +1357,14 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
        }

        // Atomically retrieve all of the other things to do.
        final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
                true /* remove */, processPausingActivities);
        NS = stops != null ? stops.size() : 0;
        if ((NF = mFinishingActivities.size()) > 0) {
            finishes = new ArrayList<>(mFinishingActivities);
            mFinishingActivities.clear();
        }
        processStoppingAndFinishingActivities(r, processPausingActivities, "idle");

        if (mStartingUsers.size() > 0) {
            startingUsers = new ArrayList<>(mStartingUsers);
        if (!mStartingUsers.isEmpty()) {
            final ArrayList<UserState> startingUsers = new ArrayList<>(mStartingUsers);
            mStartingUsers.clear();
        }

        // Stop any activities that are scheduled to do so but have been
        // waiting for the next one to start.
        for (int i = 0; i < NS; i++) {
            r = stops.get(i);
            final ActivityStack stack = r.getActivityStack();
            if (stack != null) {
                if (r.finishing) {
                    // TODO(b/137329632): Wait for idle of the right activity, not just any.
                    r.destroyIfPossible("activityIdleInternalLocked");
                } else {
                    r.stopIfPossible();
                }
            }
        }

        // Finish any activities that are scheduled to do so but have been
        // waiting for the next one to start.
        for (int i = 0; i < NF; i++) {
            r = finishes.get(i);
            final ActivityStack stack = r.getActivityStack();
            if (stack != null) {
                activityRemoved |= r.destroyImmediately(true /* removeFromApp */, "finish-idle");
            }
        }

            if (!booting) {
            // Complete user switch
            if (startingUsers != null) {
                // Complete user switch.
                for (int i = 0; i < startingUsers.size(); i++) {
                    mService.mAmInternal.finishUserSwitch(startingUsers.get(i));
                }
@@ -1413,14 +1372,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
        }

        mService.mH.post(() -> mService.mAmInternal.trimApplications());
        //dump();
        //mWindowManager.dump();

        if (activityRemoved) {
            mRootWindowContainer.resumeFocusedStacksTopActivities();
        }

        return r;
    }

    /** This doesn't just find a task, it also moves the task to front. */
@@ -1761,7 +1712,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
            stack.mForceHidden = true;
            stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
            stack.mForceHidden = false;
            activityIdleInternalLocked(null, false /* fromTimeout */,
            activityIdleInternal(null /* idleActivity */, false /* fromTimeout */,
                    true /* processPausingActivities */, null /* configuration */);

            // Move all the tasks to the bottom of the fullscreen stack
@@ -2129,19 +2080,46 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
        return false;
    }

    // TODO: Change method name to reflect what it actually does.
    final ArrayList<ActivityRecord> processStoppingActivitiesLocked(ActivityRecord idleActivity,
            boolean remove, boolean processPausingActivities) {
        ArrayList<ActivityRecord> stops = null;
    /**
     * Processes the activities to be stopped or destroyed. This should be called when the resumed
     * activities are idle or drawn.
     */
    private void processStoppingAndFinishingActivities(ActivityRecord launchedActivity,
            boolean processPausingActivities, String reason) {
        // Stop any activities that are scheduled to do so but have been waiting for the transition
        // animation to finish.
        processStoppingActivities(launchedActivity, false /* onlyUpdateVisibility */,
                processPausingActivities, reason);

        final int numFinishingActivities = mFinishingActivities.size();
        if (numFinishingActivities == 0) {
            return;
        }

        final boolean nowVisible = mRootWindowContainer.allResumedActivitiesVisible();
        for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
            ActivityRecord s = mStoppingActivities.get(activityNdx);
        // Finish any activities that are scheduled to do so but have been waiting for the next one
        // to start.
        final ArrayList<ActivityRecord> finishingActivities = new ArrayList<>(mFinishingActivities);
        mFinishingActivities.clear();
        for (int i = 0; i < numFinishingActivities; i++) {
            final ActivityRecord r = finishingActivities.get(i);
            if (r.isInHistory()) {
                r.destroyImmediately(true /* removeFromApp */, "finish-" + reason);
            }
        }
    }

            final boolean animating = s.isAnimating(TRANSITION);
    /** Stop or destroy the stopping activities if they are not interactive. */
    void processStoppingActivities(ActivityRecord launchedActivity, boolean onlyUpdateVisibility,
            boolean processPausingActivities, String reason) {
        final int numStoppingActivities = mStoppingActivities.size();
        if (numStoppingActivities == 0) {
            return;
        }
        ArrayList<ActivityRecord> readyToStopActivities = null;

            if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
                    + " animating=" + animating + " finishing=" + s.finishing);
        final boolean nowVisible = mRootWindowContainer.allResumedActivitiesVisible();
        for (int activityNdx = numStoppingActivities - 1; activityNdx >= 0; --activityNdx) {
            final ActivityRecord s = mStoppingActivities.get(activityNdx);
            if (nowVisible && s.finishing) {

                // If this activity is finishing, it is sitting on top of
@@ -2152,7 +2130,14 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
                if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s);
                s.setVisibility(false);
            }
            if (remove) {
            if (onlyUpdateVisibility) {
                continue;
            }

            final boolean animating = s.isAnimating(TRANSITION);
            if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
                    + " animating=" + animating + " finishing=" + s.finishing);

            final ActivityStack stack = s.getActivityStack();
            final boolean shouldSleepOrShutDown = stack != null
                    ? stack.shouldSleepOrShutDownActivities()
@@ -2161,23 +2146,33 @@ 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
                        removeIdleTimeoutForActivity(idleActivity);
                        scheduleIdleTimeoutLocked(idleActivity);
                    removeIdleTimeoutForActivity(launchedActivity);
                    scheduleIdleTimeout(launchedActivity);
                    continue;
                }

                if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
                    if (stops == null) {
                        stops = new ArrayList<>();
                if (readyToStopActivities == null) {
                    readyToStopActivities = new ArrayList<>();
                }
                    stops.add(s);
                readyToStopActivities.add(s);

                mStoppingActivities.remove(activityNdx);
            }
        }
        }

        return stops;
        final int numReadyStops = readyToStopActivities == null ? 0 : readyToStopActivities.size();
        for (int i = 0; i < numReadyStops; i++) {
            final ActivityRecord r = readyToStopActivities.get(i);
            if (r.isInHistory()) {
                if (r.finishing) {
                    // TODO(b/137329632): Wait for idle of the right activity, not just any.
                    r.destroyIfPossible(reason);
                } else {
                    r.stopIfPossible();
                }
            }
        }
    }

    void removeHistoryRecords(WindowProcessController app) {
@@ -2315,14 +2310,13 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
        return printed;
    }

    void scheduleIdleTimeoutLocked(ActivityRecord next) {
        if (DEBUG_IDLE) Slog.d(TAG_IDLE,
                "scheduleIdleTimeoutLocked: Callers=" + Debug.getCallers(4));
    void scheduleIdleTimeout(ActivityRecord next) {
        if (DEBUG_IDLE) Slog.d(TAG_IDLE, "scheduleIdleTimeout: Callers=" + Debug.getCallers(4));
        Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
        mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
    }

    final void scheduleIdleLocked() {
    final void scheduleIdle() {
        mHandler.sendEmptyMessage(IDLE_NOW_MSG);
    }

@@ -2616,65 +2610,84 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {

    private final class ActivityStackSupervisorHandler extends Handler {

        public ActivityStackSupervisorHandler(Looper looper) {
        ActivityStackSupervisorHandler(Looper looper) {
            super(looper);
        }

        void activityIdleInternal(ActivityRecord r, boolean processPausingActivities) {
        @Override
        public void handleMessage(Message msg) {
            synchronized (mService.mGlobalLock) {
                if (handleMessageInner(msg)) {
                    return;
                }
            }
            // The cases that some invocations cannot be locked by WM.
            switch (msg.what) {
                case RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG: {
                    final ActivityRecord r = (ActivityRecord) msg.obj;
                    String processName = null;
                    int uid = 0;
                    synchronized (mService.mGlobalLock) {
                activityIdleInternalLocked(r != null ? r.appToken : null, true /* fromTimeout */,
                        processPausingActivities, null);
                        if (r.attachedToProcess()
                                && r.isState(ActivityStack.ActivityState.RESTARTING_PROCESS)) {
                            processName = r.app.mName;
                            uid = r.app.mUid;
                        }
                    }
                    if (processName != null) {
                        mService.mAmInternal.killProcess(processName, uid,
                                "restartActivityProcessTimeout");
                    }
                } break;
            }
        }

        @Override
        public void handleMessage(Message msg) {
        private void activityIdleFromMessage(ActivityRecord idleActivity, boolean fromTimeout) {
            activityIdleInternal(idleActivity, fromTimeout,
                    fromTimeout /* processPausingActivities */, null /* config */);
        }

        /**
         * Handles the message with lock held.
         *
         * @return {@code true} if the message is handled.
         */
        private boolean handleMessageInner(Message msg) {
            switch (msg.what) {
                case REPORT_MULTI_WINDOW_MODE_CHANGED_MSG: {
                    synchronized (mService.mGlobalLock) {
                    for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
                        final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
                        r.updateMultiWindowMode();
                    }
                    }
                } break;
                case REPORT_PIP_MODE_CHANGED_MSG: {
                    synchronized (mService.mGlobalLock) {
                    for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
                        final ActivityRecord r = mPipModeChangedActivities.remove(i);
                        r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
                                false /* forceUpdate */);
                    }
                    }
                } break;
                case IDLE_TIMEOUT_MSG: {
                    if (DEBUG_IDLE) Slog.d(TAG_IDLE,
                            "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
                    // We don't at this point know if the activity is fullscreen,
                    // so we need to be conservative and assume it isn't.
                    activityIdleInternal((ActivityRecord) msg.obj,
                            true /* processPausingActivities */);
                    // We don't at this point know if the activity is fullscreen, so we need to be
                    // conservative and assume it isn't.
                    activityIdleFromMessage((ActivityRecord) msg.obj, true /* fromTimeout */);
                } break;
                case IDLE_NOW_MSG: {
                    if (DEBUG_IDLE) Slog.d(TAG_IDLE, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
                    activityIdleInternal((ActivityRecord) msg.obj,
                            false /* processPausingActivities */);
                    activityIdleFromMessage((ActivityRecord) msg.obj, false /* fromTimeout */);
                } break;
                case RESUME_TOP_ACTIVITY_MSG: {
                    synchronized (mService.mGlobalLock) {
                    mRootWindowContainer.resumeFocusedStacksTopActivities();
                    }
                } break;
                case SLEEP_TIMEOUT_MSG: {
                    synchronized (mService.mGlobalLock) {
                    if (mService.isSleepingOrShuttingDownLocked()) {
                        Slog.w(TAG, "Sleep timeout!  Sleeping now.");
                        checkReadyForSleepLocked(false /* allowDelay */);
                    }
                    }
                } break;
                case LAUNCH_TIMEOUT_MSG: {
                    synchronized (mService.mGlobalLock) {
                    if (mLaunchingActivityWakeLock.isHeld()) {
                        Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
                        if (VALIDATE_WAKE_LOCK_CALLER
@@ -2683,52 +2696,32 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
                        }
                        mLaunchingActivityWakeLock.release();
                    }
                    }
                } break;
                case LAUNCH_TASK_BEHIND_COMPLETE: {
                    synchronized (mService.mGlobalLock) {
                        ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
                    final ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
                    if (r != null) {
                        handleLaunchTaskBehindCompleteLocked(r);
                    }
                    }
                } break;
                case RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG: {
                    final ActivityRecord r = (ActivityRecord) msg.obj;
                    String processName = null;
                    int uid = 0;
                    synchronized (mService.mGlobalLock) {
                        if (r.attachedToProcess()
                                && r.isState(ActivityStack.ActivityState.RESTARTING_PROCESS)) {
                            processName = r.app.mName;
                            uid = r.app.mUid;
                        }
                    }
                    if (processName != null) {
                        mService.mAmInternal.killProcess(processName, uid,
                                "restartActivityProcessTimeout");
                    }
                } break;
                case REPORT_HOME_CHANGED_MSG: {
                    synchronized (mService.mGlobalLock) {
                    mHandler.removeMessages(REPORT_HOME_CHANGED_MSG);

                    // Start home activities on displays with no activities.
                    mRootWindowContainer.startHomeOnEmptyDisplays("homeChanged");
                    }
                } break;
                case TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG: {
                    ActivityRecord r = (ActivityRecord) msg.obj;
                    final ActivityRecord r = (ActivityRecord) msg.obj;
                    Slog.w(TAG, "Activity top resumed state loss timeout for " + r);
                    synchronized (mService.mGlobalLock) {
                    if (r.hasProcess()) {
                        mService.logAppTooSlow(r.app, r.topResumedStateLossTime,
                                "top state loss for " + r);
                    }
                    }
                    handleTopResumedStateReleased(true /* timeout */);
                } break;
                default:
                    return false;
            }
            return true;
        }
    }

+8 −12
Original line number Diff line number Diff line
@@ -1684,20 +1684,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
    public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
        final long origId = Binder.clearCallingIdentity();
        try {
            WindowProcessController proc = null;
            synchronized (mGlobalLock) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle");
                ActivityStack stack = ActivityRecord.getStackLocked(token);
                if (stack == null) {
                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
                if (r == null) {
                    return;
                }
                final ActivityRecord r = mStackSupervisor.activityIdleInternalLocked(token,
                        false /* fromTimeout */, false /* processPausingActivities */, config);
                if (r != null) {
                    proc = r.app;
                }
                if (stopProfiling && proc != null) {
                    proc.clearProfilerIfNeeded();
                mStackSupervisor.activityIdleInternal(r, false /* fromTimeout */,
                        false /* processPausingActivities */, config);
                if (stopProfiling && r.hasProcess()) {
                    r.app.clearProfilerIfNeeded();
                }
            }
        } finally {
@@ -6904,7 +6900,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
                if (mRootWindowContainer.finishDisabledPackageActivities(
                        packageName, disabledClasses, true, false, userId) && booted) {
                    mRootWindowContainer.resumeFocusedStacksTopActivities();
                    mStackSupervisor.scheduleIdleLocked();
                    mStackSupervisor.scheduleIdle();
                }

                // Clean-up disabled tasks
@@ -6931,7 +6927,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
            synchronized (mGlobalLock) {
                mRootWindowContainer.resumeFocusedStacksTopActivities();
                if (scheduleIdle) {
                    mStackSupervisor.scheduleIdleLocked();
                    mStackSupervisor.scheduleIdle();
                }
            }
        }
+2 −2
Original line number Diff line number Diff line
@@ -484,8 +484,8 @@ public class SystemServicesTestRule implements TestRule {
            spyOn(this);

            // Do not schedule idle that may touch methods outside the scope of the test.
            doNothing().when(this).scheduleIdleLocked();
            doNothing().when(this).scheduleIdleTimeoutLocked(any());
            doNothing().when(this).scheduleIdle();
            doNothing().when(this).scheduleIdleTimeout(any());
            // unit test version does not handle launch wake lock
            doNothing().when(this).acquireLaunchWakelock();
            doReturn(mock(KeyguardController.class)).when(this).getKeyguardController();