Merge "Unblock screen-on after start transaction is completed" into 24D1-dev
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 65bf241..d91785f 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -8,6 +8,16 @@
}
flag {
+ name: "wait_for_transition_on_display_switch"
+ namespace: "windowing_frontend"
+ description: "Waits for Shell transition to start before unblocking the screen after display switch"
+ bug: "301420598"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "edge_to_edge_by_default"
namespace: "windowing_frontend"
description: "Make app go edge-to-edge by default when targeting SDK 35 or greater"
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 28664dd..a4d98fa 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -730,6 +730,7 @@
mContext.getSystemService(DeviceStateManager.class).registerCallback(
new HandlerExecutor(mHandler), new DeviceStateListener());
+ mLogicalDisplayMapper.onWindowManagerReady();
scheduleTraversalLocked(false);
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 86afa9b..d40a7a3 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -41,10 +41,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.foldables.FoldGracePeriodProvider;
+import com.android.server.LocalServices;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.DisplayIdProducer;
import com.android.server.display.layout.Layout;
import com.android.server.display.utils.DebugUtils;
+import com.android.server.policy.WindowManagerPolicy;
import com.android.server.utils.FoldSettingProvider;
import java.io.PrintWriter;
@@ -189,6 +191,7 @@
* #updateLogicalDisplaysLocked} to establish which Virtual Devices own which Virtual Displays.
*/
private final ArrayMap<String, Integer> mVirtualDeviceDisplayMapping = new ArrayMap<>();
+ private WindowManagerPolicy mWindowManagerPolicy;
private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
private final DisplayIdProducer mIdProducer = (isDefault) ->
@@ -273,6 +276,10 @@
mListener.onTraversalRequested();
}
+ public void onWindowManagerReady() {
+ mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
+ }
+
public LogicalDisplay getDisplayLocked(int displayId) {
return getDisplayLocked(displayId, /* includeDisabled= */ true);
}
@@ -1114,14 +1121,22 @@
final int logicalDisplayId = displayLayout.getLogicalDisplayId();
LogicalDisplay newDisplay = getDisplayLocked(logicalDisplayId);
+ boolean newDisplayCreated = false;
if (newDisplay == null) {
newDisplay = createNewLogicalDisplayLocked(
null /*displayDevice*/, logicalDisplayId);
+ newDisplayCreated = true;
}
// Now swap the underlying display devices between the old display and the new display
final LogicalDisplay oldDisplay = getDisplayLocked(device);
if (newDisplay != oldDisplay) {
+ // Display is swapping, notify WindowManager, so it can prepare for
+ // the display switch
+ if (!newDisplayCreated && mWindowManagerPolicy != null) {
+ mWindowManagerPolicy.onDisplaySwitchStart(newDisplay.getDisplayIdLocked());
+ }
+
newDisplay.swapDisplaysLocked(oldDisplay);
}
DisplayDeviceConfig config = device.getDisplayDeviceConfig();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 994f50c..3f2e12b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5658,6 +5658,13 @@
}
}
+ @Override
+ public void onDisplaySwitchStart(int displayId) {
+ if (displayId == DEFAULT_DISPLAY) {
+ mDefaultDisplayPolicy.onDisplaySwitchStart();
+ }
+ }
+
private long getKeyguardDrawnTimeout() {
final boolean bootCompleted =
LocalServices.getService(SystemServiceManager.class).isBootCompleted();
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 5956594..2623025 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -895,6 +895,9 @@
void onScreenOff();
}
+ /** Called when the physical display starts to switch, e.g. fold/unfold. */
+ void onDisplaySwitchStart(int displayId);
+
/**
* Return whether the default display is on and not blocked by a black surface.
*/
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index 123633d..1c8e7c0 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -26,6 +26,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
+import android.os.Message;
+import android.os.Trace;
+import android.util.Log;
+import android.util.Slog;
import android.view.DisplayInfo;
import android.window.DisplayAreaInfo;
import android.window.TransitionRequestInfo;
@@ -35,6 +39,7 @@
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.wm.utils.DisplayInfoOverrides.DisplayInfoFieldsUpdater;
+import com.android.window.flags.Flags;
import java.util.Arrays;
import java.util.Objects;
@@ -65,6 +70,12 @@
WM_OVERRIDE_FIELDS.setFields(out, override);
};
+ private static final String TAG = "DeferredDisplayUpdater";
+
+ private static final String TRACE_TAG_WAIT_FOR_TRANSITION =
+ "Screen unblock: wait for transition";
+ private static final int WAIT_FOR_TRANSITION_TIMEOUT = 1000;
+
private final DisplayContent mDisplayContent;
@NonNull
@@ -88,6 +99,18 @@
@NonNull
private final DisplayInfo mOutputDisplayInfo = new DisplayInfo();
+ /** Whether {@link #mScreenUnblocker} should wait for transition to be ready. */
+ private boolean mShouldWaitForTransitionWhenScreenOn;
+
+ /** The message to notify PhoneWindowManager#finishWindowsDrawn. */
+ @Nullable
+ private Message mScreenUnblocker;
+
+ private final Runnable mScreenUnblockTimeoutRunnable = () -> {
+ Slog.e(TAG, "Timeout waiting for the display switch transition to start");
+ continueScreenUnblocking();
+ };
+
public DeferredDisplayUpdater(@NonNull DisplayContent displayContent) {
mDisplayContent = displayContent;
mNonOverrideDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
@@ -246,6 +269,7 @@
getCurrentDisplayChange(fromRotation, startBounds);
displayChange.setPhysicalDisplayChanged(true);
+ transition.addTransactionCompletedListener(this::continueScreenUnblocking);
mDisplayContent.mTransitionController.requestStartTransition(transition,
/* startTask= */ null, /* remoteTransition= */ null, displayChange);
@@ -275,6 +299,58 @@
return !Objects.equals(first.uniqueId, second.uniqueId);
}
+ @Override
+ public void onDisplayContentDisplayPropertiesPostChanged(int previousRotation, int newRotation,
+ DisplayAreaInfo newDisplayAreaInfo) {
+ // Unblock immediately in case there is no transition. This is unlikely to happen.
+ if (mScreenUnblocker != null && !mDisplayContent.mTransitionController.inTransition()) {
+ mScreenUnblocker.sendToTarget();
+ mScreenUnblocker = null;
+ }
+ }
+
+ @Override
+ public void onDisplaySwitching(boolean switching) {
+ mShouldWaitForTransitionWhenScreenOn = switching;
+ }
+
+ @Override
+ public boolean waitForTransition(@NonNull Message screenUnblocker) {
+ if (!Flags.waitForTransitionOnDisplaySwitch()) return false;
+ if (!mShouldWaitForTransitionWhenScreenOn) {
+ return false;
+ }
+ mScreenUnblocker = screenUnblocker;
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.beginAsyncSection(TRACE_TAG_WAIT_FOR_TRANSITION, screenUnblocker.hashCode());
+ }
+
+ mDisplayContent.mWmService.mH.removeCallbacks(mScreenUnblockTimeoutRunnable);
+ mDisplayContent.mWmService.mH.postDelayed(mScreenUnblockTimeoutRunnable,
+ WAIT_FOR_TRANSITION_TIMEOUT);
+ return true;
+ }
+
+ /**
+ * Continues the screen unblocking flow, could be called either on a binder thread as
+ * a result of surface transaction completed listener or from {@link WindowManagerService#mH}
+ * handler in case of timeout
+ */
+ private void continueScreenUnblocking() {
+ synchronized (mDisplayContent.mWmService.mGlobalLock) {
+ mShouldWaitForTransitionWhenScreenOn = false;
+ mDisplayContent.mWmService.mH.removeCallbacks(mScreenUnblockTimeoutRunnable);
+ if (mScreenUnblocker == null) {
+ return;
+ }
+ mScreenUnblocker.sendToTarget();
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.endAsyncSection(TRACE_TAG_WAIT_FOR_TRANSITION, mScreenUnblocker.hashCode());
+ }
+ mScreenUnblocker = null;
+ }
+ }
+
/**
* Diff result: fields are the same
*/
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 877f974..577d2a6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -477,7 +477,7 @@
private final DisplayRotation mDisplayRotation;
@Nullable final DisplayRotationCompatPolicy mDisplayRotationCompatPolicy;
DisplayFrames mDisplayFrames;
- private final DisplayUpdater mDisplayUpdater;
+ final DisplayUpdater mDisplayUpdater;
private boolean mInTouchMode;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index cc00280..90a9fee 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -777,6 +777,11 @@
return mLidState;
}
+ private void onDisplaySwitchFinished() {
+ mDisplayContent.mWallpaperController.onDisplaySwitchFinished();
+ mDisplayContent.mDisplayUpdater.onDisplaySwitching(false);
+ }
+
public void setAwake(boolean awake) {
synchronized (mLock) {
if (awake == mAwake) {
@@ -795,7 +800,7 @@
mService.mAtmService.mKeyguardController.updateDeferTransitionForAod(
mAwake /* waiting */);
if (!awake) {
- mDisplayContent.mWallpaperController.onDisplaySwitchFinished();
+ onDisplaySwitchFinished();
}
}
}
@@ -864,7 +869,7 @@
/** It is called after {@link #finishScreenTurningOn}. This runs on PowerManager's thread. */
public void screenTurnedOn() {
- mDisplayContent.mWallpaperController.onDisplaySwitchFinished();
+ onDisplaySwitchFinished();
}
public void screenTurnedOff() {
@@ -2171,6 +2176,11 @@
mDisplayContent.mTransitionController.getCollectingTransitionId();
}
+ /** If this is called, expect that there will be an onDisplayChanged about unique id. */
+ public void onDisplaySwitchStart() {
+ mDisplayContent.mDisplayUpdater.onDisplaySwitching(true);
+ }
+
@NavigationBarPosition
int navigationBarPosition(int displayRotation) {
if (mNavigationBar != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayUpdater.java b/services/core/java/com/android/server/wm/DisplayUpdater.java
index e611177..918b180 100644
--- a/services/core/java/com/android/server/wm/DisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DisplayUpdater.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import android.annotation.NonNull;
+import android.os.Message;
import android.view.Surface;
import android.window.DisplayAreaInfo;
@@ -49,4 +50,16 @@
@Surface.Rotation int previousRotation, @Surface.Rotation int newRotation,
@NonNull DisplayAreaInfo newDisplayAreaInfo) {
}
+
+ /**
+ * Called with {@code true} when physical display is going to switch. And {@code false} when
+ * the display is turned on or the device goes to sleep.
+ */
+ default void onDisplaySwitching(boolean switching) {
+ }
+
+ /** Returns {@code true} if the transition will control when to turn on the screen. */
+ default boolean waitForTransition(@NonNull Message screenUnBlocker) {
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 7edc3a2..d30ddca 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -112,6 +112,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.function.Predicate;
/**
@@ -233,6 +234,9 @@
*/
private ArrayList<Task> mTransientHideTasks;
+ @VisibleForTesting
+ ArrayList<Runnable> mTransactionCompletedListeners = null;
+
/** Custom activity-level animation options and callbacks. */
private TransitionInfo.AnimationOptions mOverrideOptions;
private IRemoteCallback mClientAnimationStartCallback = null;
@@ -1624,6 +1628,14 @@
commitVisibleActivities(transaction);
commitVisibleWallpapers();
+ if (mTransactionCompletedListeners != null) {
+ for (int i = 0; i < mTransactionCompletedListeners.size(); i++) {
+ final Runnable listener = mTransactionCompletedListeners.get(i);
+ transaction.addTransactionCompletedListener(Runnable::run,
+ (stats) -> listener.run());
+ }
+ }
+
// Fall-back to the default display if there isn't one participating.
final DisplayContent primaryDisplay = !mTargetDisplays.isEmpty() ? mTargetDisplays.get(0)
: mController.mAtm.mRootWindowContainer.getDefaultDisplay();
@@ -1846,6 +1858,17 @@
}
/**
+ * Adds a listener that will be executed after the start transaction of this transition
+ * is presented on the screen, the listener will be executed on a binder thread
+ */
+ void addTransactionCompletedListener(Runnable listener) {
+ if (mTransactionCompletedListeners == null) {
+ mTransactionCompletedListeners = new ArrayList<>();
+ }
+ mTransactionCompletedListeners.add(listener);
+ }
+
+ /**
* Checks if the transition contains order changes.
*
* This is a shallow check that doesn't account for collection in parallel, unlike
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8edd94b..8c6478d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8007,6 +8007,10 @@
}
boolean allWindowsDrawn = false;
synchronized (mGlobalLock) {
+ if (mRoot.getDefaultDisplay().mDisplayUpdater.waitForTransition(message)) {
+ // Use the ready-to-play of transition as the signal.
+ return;
+ }
container.waitForAllWindowsDrawn();
mWindowPlacerLocked.requestTraversal();
mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 357999a..ad5c6e0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -79,12 +79,16 @@
import androidx.test.filters.SmallTest;
import com.android.internal.foldables.FoldGracePeriodProvider;
+import com.android.internal.util.test.LocalServiceKeeperRule;
+import com.android.server.LocalServices;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.DisplayIdProducer;
import com.android.server.display.layout.Layout;
+import com.android.server.policy.WindowManagerPolicy;
import com.android.server.utils.FoldSettingProvider;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -124,6 +128,9 @@
private DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy;
+ @Rule
+ public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule();
+
@Mock LogicalDisplayMapper.Listener mListenerMock;
@Mock Context mContextMock;
@Mock FoldSettingProvider mFoldSettingProviderMock;
@@ -133,6 +140,7 @@
@Mock IThermalService mIThermalServiceMock;
@Mock DisplayManagerFlags mFlagsMock;
@Mock DisplayAdapter mDisplayAdapterMock;
+ @Mock WindowManagerPolicy mWindowManagerPolicy;
@Captor ArgumentCaptor<LogicalDisplay> mDisplayCaptor;
@Captor ArgumentCaptor<Integer> mDisplayEventCaptor;
@@ -143,6 +151,9 @@
System.setProperty("dexmaker.share_classloader", "true");
MockitoAnnotations.initMocks(this);
+ mLocalServiceKeeperRule.overrideLocalService(WindowManagerPolicy.class,
+ mWindowManagerPolicy);
+
mDeviceStateToLayoutMapSpy =
spy(new DeviceStateToLayoutMap(mIdProducer, mFlagsMock, NON_EXISTING_FILE));
mDisplayDeviceRepo = new DisplayDeviceRepository(
@@ -194,6 +205,7 @@
mDisplayDeviceRepo,
mListenerMock, new DisplayManagerService.SyncRoot(), mHandler,
mDeviceStateToLayoutMapSpy, mFlagsMock);
+ mLogicalDisplayMapper.onWindowManagerReady();
}
@@ -770,6 +782,46 @@
}
@Test
+ public void testDisplaySwappedAfterDeviceStateChange_windowManagerIsNotified() {
+ FoldableDisplayDevices foldableDisplayDevices = createFoldableDeviceStateToLayoutMap();
+ mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_OPEN,
+ /* isOverrideActive= */ false);
+ mLogicalDisplayMapper.onEarlyInteractivityChange(true);
+ mLogicalDisplayMapper.onBootCompleted();
+ advanceTime(1000);
+ clearInvocations(mWindowManagerPolicy);
+
+ // Switch from 'inner' to 'outer' display (fold a foldable device)
+ mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_CLOSED,
+ /* isOverrideActive= */ false);
+ // Continue folding device state transition by turning off the inner display
+ foldableDisplayDevices.mInner.setState(STATE_OFF);
+ notifyDisplayChanges(foldableDisplayDevices.mOuter);
+ advanceTime(TIMEOUT_STATE_TRANSITION_MILLIS);
+
+ verify(mWindowManagerPolicy).onDisplaySwitchStart(DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testCreateNewLogicalDisplay_windowManagerIsNotNotifiedAboutSwitch() {
+ DisplayDevice device1 = createDisplayDevice(TYPE_EXTERNAL, 600, 800,
+ FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
+ when(mDeviceStateToLayoutMapSpy.size()).thenReturn(1);
+ LogicalDisplay display1 = add(device1);
+
+ assertTrue(display1.isEnabledLocked());
+
+ DisplayDevice device2 = createDisplayDevice(TYPE_INTERNAL, 600, 800,
+ FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
+ when(mDeviceStateToLayoutMapSpy.size()).thenReturn(2);
+ add(device2);
+
+ // As it is not a display switch but adding a new display, we should not notify
+ // about display switch start to window manager
+ verify(mWindowManagerPolicy, never()).onDisplaySwitchStart(anyInt());
+ }
+
+ @Test
public void testDoNotWaitForSleepWhenFoldSettingStayAwake() {
// Test device should be marked ready for transition immediately when 'Continue using app
// on fold' set to 'Always'
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
index 99d354a..e79faed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
@@ -31,6 +31,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
+import android.os.Message;
import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
@@ -60,6 +61,8 @@
private int mColorMode;
private int mLogicalDensityDpi;
+ private final Message mScreenUnblocker = mock(Message.class);
+
@Override
protected void onBeforeSystemServicesCreated() {
// Set other flags to their default values
@@ -72,12 +75,11 @@
public void before() {
mockTransitionsController(/* enabled= */ true);
mockRemoteDisplayChangeController();
+ performInitialDisplayUpdate();
}
@Test
public void testUpdate_deferrableFieldChangedTransitionStarted_deferrableFieldUpdated() {
- performInitialDisplayUpdate();
-
mUniqueId = "old";
Runnable onUpdated = mock(Runnable.class);
mDisplayContent.requestDisplayUpdate(onUpdated);
@@ -106,8 +108,6 @@
@Test
public void testUpdate_nonDeferrableUpdateAndTransitionDeferred_nonDeferrableFieldUpdated() {
- performInitialDisplayUpdate();
-
// Update only color mode (non-deferrable field) and keep the same unique id
mUniqueId = "initial_unique_id";
mColorMode = 123;
@@ -120,8 +120,6 @@
@Test
public void testUpdate_nonDeferrableUpdateTwiceAndTransitionDeferred_fieldHasLatestValue() {
- performInitialDisplayUpdate();
-
// Update only color mode (non-deferrable field) and keep the same unique id
mUniqueId = "initial_unique_id";
mColorMode = 123;
@@ -162,7 +160,6 @@
@Test
public void testUpdate_deferrableFieldUpdatedTransitionPending_fieldNotUpdated() {
- performInitialDisplayUpdate();
mUniqueId = "old";
Runnable onUpdated = mock(Runnable.class);
mDisplayContent.requestDisplayUpdate(onUpdated);
@@ -180,7 +177,6 @@
@Test
public void testTwoDisplayUpdates_transitionStarted_displayUpdated() {
- performInitialDisplayUpdate();
mUniqueId = "old";
Runnable onUpdated = mock(Runnable.class);
mDisplayContent.requestDisplayUpdate(onUpdated);
@@ -211,6 +207,51 @@
assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("new2");
}
+ @Test
+ public void testWaitForTransition_displaySwitching_waitsForTransitionToBeStarted() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH);
+ mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ true);
+ boolean willWait = mDisplayContent.mDisplayUpdater.waitForTransition(mScreenUnblocker);
+ assertThat(willWait).isTrue();
+ mUniqueId = "new";
+ mDisplayContent.requestDisplayUpdate(mock(Runnable.class));
+ when(mDisplayContent.mTransitionController.inTransition()).thenReturn(true);
+ captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true);
+
+ // Verify that screen is not unblocked yet as the start transaction hasn't been presented
+ verify(mScreenUnblocker, never()).sendToTarget();
+
+ when(mDisplayContent.mTransitionController.inTransition()).thenReturn(false);
+ final Transition transition = captureRequestedTransition().getValue();
+ makeTransitionTransactionCompleted(transition);
+
+ // Verify that screen is unblocked as start transaction of the transition
+ // has been completed
+ verify(mScreenUnblocker).sendToTarget();
+ }
+
+ @Test
+ public void testWaitForTransition_displayNotSwitching_doesNotWait() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH);
+ mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ false);
+
+ boolean willWait = mDisplayContent.mDisplayUpdater.waitForTransition(mScreenUnblocker);
+
+ assertThat(willWait).isFalse();
+ verify(mScreenUnblocker, never()).sendToTarget();
+ }
+
+ @Test
+ public void testWaitForTransition_displayIsSwitchingButFlagDisabled_doesNotWait() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH);
+ mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ true);
+
+ boolean willWait = mDisplayContent.mDisplayUpdater.waitForTransition(mScreenUnblocker);
+
+ assertThat(willWait).isFalse();
+ verify(mScreenUnblocker, never()).sendToTarget();
+ }
+
private void mockTransitionsController(boolean enabled) {
spyOn(mDisplayContent.mTransitionController);
when(mDisplayContent.mTransitionController.isShellTransitionsEnabled()).thenReturn(enabled);
@@ -232,6 +273,23 @@
return callbackCaptor;
}
+ private ArgumentCaptor<Transition> captureRequestedTransition() {
+ ArgumentCaptor<Transition> callbackCaptor =
+ ArgumentCaptor.forClass(Transition.class);
+ verify(mDisplayContent.mTransitionController, atLeast(1))
+ .requestStartTransition(callbackCaptor.capture(), any(), any(), any());
+ return callbackCaptor;
+ }
+
+ private void makeTransitionTransactionCompleted(Transition transition) {
+ if (transition.mTransactionCompletedListeners != null) {
+ for (int i = 0; i < transition.mTransactionCompletedListeners.size(); i++) {
+ final Runnable listener = transition.mTransactionCompletedListeners.get(i);
+ listener.run();
+ }
+ }
+ }
+
private void performInitialDisplayUpdate() {
mUniqueId = "initial_unique_id";
mColorMode = 0;
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 1233686..00a8842 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -167,6 +167,10 @@
}
@Override
+ public void onDisplaySwitchStart(int displayId) {
+ }
+
+ @Override
public boolean okToAnimate(boolean ignoreScreenOn) {
return mOkToAnimate;
}