diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 47e880a3fce20..8eed76505847a 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -2094,14 +2094,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call && !mScreenOffBecauseOfProximity) { setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF); unblockScreenOn(); - mWindowManagerPolicy.screenTurnedOff(mDisplayId); + mWindowManagerPolicy.screenTurnedOff(mDisplayId, mIsInTransition); } else if (!isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_OFF) { // We told policy already that screen was turning off, but now we changed our minds. // Complete the full state transition on -> turningOff -> off. unblockScreenOff(); - mWindowManagerPolicy.screenTurnedOff(mDisplayId); + mWindowManagerPolicy.screenTurnedOff(mDisplayId, mIsInTransition); setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF); } if (!isOff diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index f064f83393c0b..74d87cd60b685 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -4756,11 +4756,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Called on the DisplayManager's DisplayPowerController thread. @Override - public void screenTurnedOff(int displayId) { + public void screenTurnedOff(int displayId, boolean isSwappingDisplay) { if (DEBUG_WAKEUP) Slog.i(TAG, "Display" + displayId + " turned off..."); if (displayId == DEFAULT_DISPLAY) { - updateScreenOffSleepToken(true); + updateScreenOffSleepToken(true, isSwappingDisplay); mRequestedOrSleepingDefaultDisplay = false; mDefaultDisplayPolicy.screenTurnedOff(); synchronized (mLock) { @@ -4811,7 +4811,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (displayId == DEFAULT_DISPLAY) { Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", 0 /* cookie */); - updateScreenOffSleepToken(false); + updateScreenOffSleepToken(false /* acquire */, false /* isSwappingDisplay */); mDefaultDisplayPolicy.screenTurnedOn(screenOnListener); mBootAnimationDismissable = false; @@ -5330,9 +5330,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // TODO (multidisplay): Support multiple displays in WindowManagerPolicy. - private void updateScreenOffSleepToken(boolean acquire) { + private void updateScreenOffSleepToken(boolean acquire, boolean isSwappingDisplay) { if (acquire) { - mScreenOffSleepTokenAcquirer.acquire(DEFAULT_DISPLAY); + mScreenOffSleepTokenAcquirer.acquire(DEFAULT_DISPLAY, isSwappingDisplay); } else { mScreenOffSleepTokenAcquirer.release(DEFAULT_DISPLAY); } diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 77007fa229a27..cd63caa063f09 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -829,8 +829,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { /** * Called when the display has turned off. + * @param displayId The display to apply to. + * @param isSwappingDisplay Whether the display is swapping to another physical display. */ - public void screenTurnedOff(int displayId); + void screenTurnedOff(int displayId, boolean isSwappingDisplay); public interface ScreenOnListener { void onScreenOn(); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index d872ada1fd0c3..f3d7448f19aba 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -8417,6 +8417,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return inTransitionSelfOrParent(); } + boolean isDisplaySleepingAndSwapping() { + for (int i = mDisplayContent.mAllSleepTokens.size() - 1; i >= 0; i--) { + RootWindowContainer.SleepToken sleepToken = mDisplayContent.mAllSleepTokens.get(i); + if (sleepToken.isDisplaySwapping()) { + return true; + } + } + return false; + } + /** * Whether this activity is letterboxed for fixed orientation. If letterboxed due to fixed * orientation then aspect ratio restrictions are also already respected. diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 2792f4265caa5..9123ffa329dff 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -140,6 +140,13 @@ public abstract class ActivityTaskManagerInternal { */ void acquire(int displayId); + /** + * Acquires a sleep token. + * @param displayId The display to apply to. + * @param isSwappingDisplay Whether the display is swapping to another physical display. + */ + void acquire(int displayId, boolean isSwappingDisplay); + /** * Releases the sleep token. * @param displayId The display to apply to. diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index f6e92a6429003..863bf94b13c1b 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -4747,10 +4747,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void acquire(int displayId) { + acquire(displayId, false /* isSwappingDisplay */); + } + + @Override + public void acquire(int displayId, boolean isSwappingDisplay) { synchronized (mGlobalLock) { if (!mSleepTokens.contains(displayId)) { mSleepTokens.append(displayId, - mRootWindowContainer.createSleepToken(mTag, displayId)); + mRootWindowContainer.createSleepToken(mTag, displayId, + isSwappingDisplay)); updateSleepIfNeededLocked(); } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 9b9337e24f53d..371480884391a 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -179,6 +179,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // How long we can hold the launch wake lock before giving up. private static final int LAUNCH_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; + // How long we delay processing the stopping and finishing activities. + private static final int SCHEDULE_FINISHING_STOPPING_ACTIVITY_MS = 200; + /** How long we wait until giving up on the activity telling us it released the top state. */ private static final int TOP_RESUMED_STATE_LOSS_TIMEOUT = 500; @@ -1933,13 +1936,15 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { boolean processPausingActivities, String reason) { // Stop any activities that are scheduled to do so but have been waiting for the transition // animation to finish. + boolean displaySwapping = false; ArrayList readyToStopActivities = null; for (int i = mStoppingActivities.size() - 1; i >= 0; --i) { final ActivityRecord s = mStoppingActivities.get(i); final boolean animating = s.isInTransition(); + displaySwapping |= s.isDisplaySleepingAndSwapping(); ProtoLog.v(WM_DEBUG_STATES, "Stopping %s: nowVisible=%b animating=%b " + "finishing=%s", s, s.nowVisible, animating, s.finishing); - if (!animating || mService.mShuttingDown) { + if ((!animating && !displaySwapping) || mService.mShuttingDown) { if (!processPausingActivities && s.isState(PAUSING)) { // Defer processing pausing activities in this iteration and reschedule // a delayed idle to reprocess it again @@ -1958,6 +1963,16 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } } + // Stopping activities are deferred processing if the display is swapping. Check again + // later to ensure the stopping activities can be stopped after display swapped. + if (displaySwapping) { + mHandler.postDelayed(() -> { + synchronized (mService.mGlobalLock) { + scheduleProcessStoppingAndFinishingActivitiesIfNeeded(); + } + }, SCHEDULE_FINISHING_STOPPING_ACTIVITY_MS); + } + final int numReadyStops = readyToStopActivities == null ? 0 : readyToStopActivities.size(); for (int i = 0; i < numReadyStops; i++) { final ActivityRecord r = readyToStopActivities.get(i); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index a4e08dfb1e3bd..76afdc387dbd7 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2569,6 +2569,10 @@ class RootWindowContainer extends WindowContainer } SleepToken createSleepToken(String tag, int displayId) { + return createSleepToken(tag, displayId, false /* isSwappingDisplay */); + } + + SleepToken createSleepToken(String tag, int displayId, boolean isSwappingDisplay) { final DisplayContent display = getDisplayContent(displayId); if (display == null) { throw new IllegalArgumentException("Invalid display: " + displayId); @@ -2577,7 +2581,7 @@ class RootWindowContainer extends WindowContainer final int tokenKey = makeSleepTokenKey(tag, displayId); SleepToken token = mSleepTokens.get(tokenKey); if (token == null) { - token = new SleepToken(tag, displayId); + token = new SleepToken(tag, displayId, isSwappingDisplay); mSleepTokens.put(tokenKey, token); display.mAllSleepTokens.add(token); ProtoLog.d(WM_DEBUG_STATES, "Create sleep token: tag=%s, displayId=%d", tag, displayId); @@ -3526,18 +3530,34 @@ class RootWindowContainer extends WindowContainer private final String mTag; private final long mAcquireTime; private final int mDisplayId; + private final boolean mIsSwappingDisplay; final int mHashKey; - SleepToken(String tag, int displayId) { + // The display could remain in sleep after the physical display swapped, adding a 1 + // seconds display swap timeout to prevent activities staying in PAUSED state. + // Otherwise, the sleep token should be removed once display turns back on after swapped. + private static final long DISPLAY_SWAP_TIMEOUT = 1000; + + SleepToken(String tag, int displayId, boolean isSwappingDisplay) { mTag = tag; mDisplayId = displayId; mAcquireTime = SystemClock.uptimeMillis(); + mIsSwappingDisplay = isSwappingDisplay; mHashKey = makeSleepTokenKey(mTag, mDisplayId); } + public boolean isDisplaySwapping() { + long now = SystemClock.uptimeMillis(); + if (now - mAcquireTime > DISPLAY_SWAP_TIMEOUT) { + return false; + } + return mIsSwappingDisplay; + } + @Override public String toString() { return "{\"" + mTag + "\", display " + mDisplayId + + (mIsSwappingDisplay ? " is swapping " : "") + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}"; } diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index e2db2e6b19e1b..148dd8dfd25a2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -147,7 +147,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public void screenTurnedOff(int displayId) { + public void screenTurnedOff(int displayId, boolean isSwappingDisplay) { } @Override