diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 4660757024095..b3fafa2137660 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1575,8 +1575,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mAtmService.getTaskChangeNotificationController() .notifyTaskRequestedOrientationChanged(task.mTaskId, orientation); } - // Currently there is no use case from non-activity. - if (handleTopActivityLaunchingInDifferentOrientation(r, true /* checkOpening */)) { + // The orientation source may not be the top if it uses SCREEN_ORIENTATION_BEHIND. + final ActivityRecord topCandidate = !r.mVisibleRequested ? topRunningActivity() : r; + if (handleTopActivityLaunchingInDifferentOrientation( + topCandidate, r, true /* checkOpening */)) { // Display orientation should be deferred until the top fixed rotation is finished. return false; } @@ -1593,7 +1595,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** * Returns a valid rotation if the activity can use different orientation than the display. - * Otherwise {@link #ROTATION_UNDEFINED}. + * Otherwise {@link android.app.WindowConfiguration#ROTATION_UNDEFINED}. */ @Rotation int rotationForActivityInDifferentOrientation(@NonNull ActivityRecord r) { @@ -1603,6 +1605,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM) { return ROTATION_UNDEFINED; } + if (r.mOrientation == ActivityInfo.SCREEN_ORIENTATION_BEHIND) { + final ActivityRecord nextCandidate = getActivity( + a -> a.mOrientation != SCREEN_ORIENTATION_UNSET + && a.mOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND, + r, false /* includeBoundary */, true /* traverseTopToBottom */); + if (nextCandidate != null) { + r = nextCandidate; + } + } if (r.inMultiWindowMode() || r.getRequestedConfigurationOrientation(true /* forDisplay */) == getConfiguration().orientation) { return ROTATION_UNDEFINED; @@ -1616,18 +1627,25 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return rotation; } + boolean handleTopActivityLaunchingInDifferentOrientation(@NonNull ActivityRecord r, + boolean checkOpening) { + return handleTopActivityLaunchingInDifferentOrientation(r, r, checkOpening); + } + /** * We need to keep display rotation fixed for a while when the activity in different orientation * is launching until the launch animation is done to avoid showing the previous activity * inadvertently in a wrong orientation. * * @param r The launching activity which may change display orientation. + * @param orientationSrc It may be different from {@param r} if the launching activity uses + * "behind" orientation. * @param checkOpening Whether to check if the activity is animating by transition. Set to * {@code true} if the caller is not sure whether the activity is launching. * @return {@code true} if the fixed rotation is started. */ - boolean handleTopActivityLaunchingInDifferentOrientation(@NonNull ActivityRecord r, - boolean checkOpening) { + private boolean handleTopActivityLaunchingInDifferentOrientation(@NonNull ActivityRecord r, + @NonNull ActivityRecord orientationSrc, boolean checkOpening) { if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM) { return false; } @@ -1676,7 +1694,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // animation is not running (it may be swiping to home). return false; } - final int rotation = rotationForActivityInDifferentOrientation(r); + final int rotation = rotationForActivityInDifferentOrientation(orientationSrc); if (rotation == ROTATION_UNDEFINED) { // The display rotation won't be changed by current top activity. The client side // adjustments of previous rotated activity should be cleared earlier. Otherwise if diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 118f159bee7bc..09f3b0c26a2fb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -1092,6 +1093,25 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(ROTATION_180, dc.getRotation()); } + @Test + public void testOrientationBehind() { + final ActivityRecord prev = new ActivityBuilder(mAtm).setCreateTask(true) + .setScreenOrientation(getRotatedOrientation(mDisplayContent)).build(); + prev.mVisibleRequested = false; + final ActivityRecord top = new ActivityBuilder(mAtm).setCreateTask(true) + .setScreenOrientation(SCREEN_ORIENTATION_BEHIND).build(); + assertNotEquals(WindowConfiguration.ROTATION_UNDEFINED, + mDisplayContent.rotationForActivityInDifferentOrientation(top)); + + mDisplayContent.requestTransitionAndLegacyPrepare(WindowManager.TRANSIT_OPEN, 0); + top.setVisibility(true); + mDisplayContent.updateOrientation(); + // The top uses "behind", so the orientation is decided by the previous. + assertEquals(prev, mDisplayContent.getLastOrientationSource()); + // The top will use the rotation from "prev" with fixed rotation. + assertTrue(top.hasFixedRotationTransform()); + } + @Test public void testFixedToUserRotationChanged() { final DisplayContent dc = createNewDisplay(); @@ -1650,8 +1670,7 @@ public class DisplayContentTests extends WindowTestsBase { // The condition should reject using fixed rotation because the resumed client in real case // might get display info immediately. And the fixed rotation adjustments haven't arrived // client side so the info may be inconsistent with the requested orientation. - verify(mDisplayContent).handleTopActivityLaunchingInDifferentOrientation(eq(app), - eq(true) /* checkOpening */); + verify(mDisplayContent).updateOrientation(eq(app), anyBoolean()); assertFalse(app.isFixedRotationTransforming()); assertFalse(mDisplayContent.hasTopFixedRotationLaunchingApp()); }