diff options
author | 2025-02-19 13:51:53 -0800 | |
---|---|---|
committer | 2025-02-19 13:51:53 -0800 | |
commit | e93897907ca2e9a6fb50258052c1069a19c8253b (patch) | |
tree | 2462db6b35308ef238cae59e7820c70649459cea | |
parent | 2704560978650d42abb1222ec10fe7d8f4f374e9 (diff) | |
parent | bfc2dd4445e2fbe275de09cd73a5c24b17c4a7f8 (diff) |
Merge "Add transition support to presentation" into main
4 files changed, 100 insertions, 27 deletions
diff --git a/services/core/java/com/android/server/wm/PresentationController.java b/services/core/java/com/android/server/wm/PresentationController.java index 69463433827f..b3cff9c6cc3d 100644 --- a/services/core/java/com/android/server/wm/PresentationController.java +++ b/services/core/java/com/android/server/wm/PresentationController.java @@ -56,11 +56,6 @@ class PresentationController { ProtoLog.v(WmProtoLogGroups.WM_DEBUG_PRESENTATION, "Presentation added to display %d: %s", win.getDisplayId(), win); mPresentingDisplayIds.add(win.getDisplayId()); - if (enablePresentationForConnectedDisplays()) { - // A presentation hides all activities behind on the same display. - win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null, - /*notifyClients=*/ true); - } win.mWmService.mDisplayManagerInternal.onPresentation(displayId, /*isShown=*/ true); } @@ -76,11 +71,6 @@ class PresentationController { if (displayIdIndex != -1) { mPresentingDisplayIds.remove(displayIdIndex); } - if (enablePresentationForConnectedDisplays()) { - // A presentation hides all activities behind on the same display. - win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null, - /*notifyClients=*/ true); - } win.mWmService.mDisplayManagerInternal.onPresentation(displayId, /*isShown=*/ false); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 0b20911bcab2..8aed91b2dc66 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -157,6 +157,7 @@ import static com.android.server.wm.WindowManagerServiceDumpProto.POLICY; import static com.android.server.wm.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER; import static com.android.server.wm.WindowManagerServiceDumpProto.WINDOW_FRAMES_VALID; import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions; +import static com.android.window.flags.Flags.enablePresentationForConnectedDisplays; import static com.android.window.flags.Flags.multiCrop; import static com.android.window.flags.Flags.setScPropertiesInClient; @@ -1820,8 +1821,28 @@ public class WindowManagerService extends IWindowManager.Stub final boolean hideSystemAlertWindows = shouldHideNonSystemOverlayWindow(win); win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows); - res |= addWindowInner(win, displayPolicy, activity, displayContent, outInsetsState, - outAttachedFrame, outActiveControls, client, outSizeCompatScale, attrs); + // Only a presentation window needs a transition because its visibility affets the + // lifecycle of apps below (b/390481865). + if (enablePresentationForConnectedDisplays() && win.isPresentation()) { + Transition transition = null; + if (!win.mTransitionController.isCollecting()) { + transition = win.mTransitionController.createAndStartCollecting(TRANSIT_OPEN); + } + win.mTransitionController.collect(win.mToken); + res |= addWindowInner(win, displayPolicy, activity, displayContent, outInsetsState, + outAttachedFrame, outActiveControls, client, outSizeCompatScale, attrs); + // A presentation hides all activities behind on the same display. + win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null, + /*notifyClients=*/ true); + win.mTransitionController.getCollectingTransition().setReady(win.mToken, true); + if (transition != null) { + win.mTransitionController.requestStartTransition(transition, null, + null /* remoteTransition */, null /* displayChange */); + } + } else { + res |= addWindowInner(win, displayPolicy, activity, displayContent, outInsetsState, + outAttachedFrame, outActiveControls, client, outSizeCompatScale, attrs); + } } Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 9f1289b2c12a..92ad2cec364b 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -95,6 +95,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType; +import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME; import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER; import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET; @@ -182,6 +183,7 @@ import static com.android.server.wm.WindowStateProto.UNRESTRICTED_KEEP_CLEAR_ARE import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY; import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES; +import static com.android.window.flags.Flags.enablePresentationForConnectedDisplays; import static com.android.window.flags.Flags.surfaceTrustedOverlay; import android.annotation.CallSuper; @@ -2297,11 +2299,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP dc.updateImeInputAndControlTarget(null); } - final int type = mAttrs.type; - - if (isPresentation()) { - mWmService.mPresentationController.onPresentationRemoved(this); - } // Check if window provides non decor insets before clearing its provided insets. final boolean windowProvidesDisplayDecorInsets = providesDisplayDecorInsets(); @@ -2442,11 +2439,33 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - removeImmediately(); - mWmService.updateFocusedWindowLocked(isFocused() - ? UPDATE_FOCUS_REMOVING_FOCUS - : UPDATE_FOCUS_NORMAL, - true /*updateInputWindows*/); + // Only a presentation window needs a transition because its visibility affets the + // lifecycle of apps below (b/390481865). + if (enablePresentationForConnectedDisplays() && isPresentation()) { + Transition transition = null; + if (!mTransitionController.isCollecting()) { + transition = mTransitionController.createAndStartCollecting(TRANSIT_CLOSE); + } + mTransitionController.collect(mToken); + mAnimatingExit = true; + mRemoveOnExit = true; + mToken.setVisibleRequested(false); + mWmService.mPresentationController.onPresentationRemoved(this); + // A presentation hides all activities behind on the same display. + mDisplayContent.ensureActivitiesVisible(/*starting=*/ null, + /*notifyClients=*/ true); + mTransitionController.getCollectingTransition().setReady(mToken, true); + if (transition != null) { + mTransitionController.requestStartTransition(transition, null, + null /* remoteTransition */, null /* displayChange */); + } + } else { + removeImmediately(); + mWmService.updateFocusedWindowLocked(isFocused() + ? UPDATE_FOCUS_REMOVING_FOCUS + : UPDATE_FOCUS_NORMAL, + true /*updateInputWindows*/); + } } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java index db90c28ec7df..7c8a8835c3b8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java @@ -17,14 +17,18 @@ package com.android.server.wm; import static android.view.Display.FLAG_PRESENTATION; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_OPEN; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.window.flags.Flags.FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; +import android.annotation.NonNull; import android.graphics.Rect; import android.os.UserHandle; import android.os.UserManager; @@ -41,6 +45,7 @@ import android.view.WindowManagerGlobal; import androidx.test.filters.SmallTest; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -53,9 +58,16 @@ import org.junit.runner.RunWith; @RunWith(WindowTestRunner.class) public class PresentationControllerTests extends WindowTestsBase { + TestTransitionPlayer mPlayer; + + @Before + public void setUp() { + mPlayer = registerTestTransitionPlayer(); + } + @EnableFlags(FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS) @Test - public void testPresentationHidesActivitiesBehind() { + public void testPresentationShowAndHide() { final DisplayInfo displayInfo = new DisplayInfo(); displayInfo.copyFrom(mDisplayInfo); displayInfo.flags = FLAG_PRESENTATION; @@ -64,7 +76,6 @@ public class PresentationControllerTests extends WindowTestsBase { doReturn(dc).when(mWm.mRoot).getDisplayContentOrCreate(displayId); final ActivityRecord activity = createActivityRecord(createTask(dc)); assertTrue(activity.isVisible()); - doReturn(true).when(() -> UserManager.isVisibleBackgroundUsersEnabled()); final int uid = 100000; // uid for non-system user final Session session = createTestSession(mAtm, 1234 /* pid */, uid); @@ -72,16 +83,48 @@ public class PresentationControllerTests extends WindowTestsBase { doReturn(false).when(mWm.mUmInternal).isUserVisible(eq(userId), eq(displayId)); final WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.TYPE_PRESENTATION); - final IWindow clientWindow = new TestIWindow(); + + // Show a Presentation window, which requests the activity to be stopped. final int result = mWm.addWindow(session, clientWindow, params, View.VISIBLE, displayId, userId, WindowInsets.Type.defaultVisible(), null, new InsetsState(), new InsetsSourceControl.Array(), new Rect(), new float[1]); assertTrue(result >= WindowManagerGlobal.ADD_OKAY); + assertFalse(activity.isVisibleRequested()); + assertTrue(activity.isVisible()); + final WindowState window = mWm.windowForClientLocked(session, clientWindow, false); + window.mHasSurface = true; + final Transition addTransition = window.mTransitionController.getCollectingTransition(); + assertEquals(TRANSIT_OPEN, addTransition.mType); + assertTrue(addTransition.isInTransition(window)); + assertTrue(addTransition.isInTransition(activity)); + + // Completing the transition makes the activity invisible. + completeTransition(addTransition, /*abortSync=*/ true); assertFalse(activity.isVisible()); - final WindowState window = mWm.windowForClientLocked(session, clientWindow, false); - window.removeImmediately(); + // Remove a Presentation window, which requests the activity to be resumed back. + window.removeIfPossible(); + final Transition removeTransition = window.mTransitionController.getCollectingTransition(); + assertEquals(TRANSIT_CLOSE, removeTransition.mType); + assertTrue(removeTransition.isInTransition(window)); + assertTrue(removeTransition.isInTransition(activity)); + assertTrue(activity.isVisibleRequested()); + assertFalse(activity.isVisible()); + + // Completing the transition makes the activity visible. + completeTransition(removeTransition, /*abortSync=*/ false); assertTrue(activity.isVisible()); } + + private void completeTransition(@NonNull Transition transition, boolean abortSync) { + final ActionChain chain = ActionChain.testFinish(transition); + if (abortSync) { + // Forcefully finishing the active sync for testing purpose. + mWm.mSyncEngine.abort(transition.getSyncId()); + } else { + transition.onTransactionReady(transition.getSyncId(), mTransaction); + } + transition.finishTransition(chain); + } } |