diff options
8 files changed, 108 insertions, 76 deletions
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 111808b54802..2eb6d2a4e6b1 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -31,14 +31,11 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_PRIVATE; import static android.view.Display.INVALID_DISPLAY; import static android.view.InsetsState.TYPE_IME; -import static android.view.InsetsState.TYPE_NAVIGATION_BAR; -import static android.view.InsetsState.TYPE_TOP_BAR; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.View.GONE; -import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_TOP; @@ -173,7 +170,6 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; import android.view.View; -import android.view.ViewRootImpl; import android.view.WindowManager; import android.view.WindowManagerPolicyConstants.PointerEventListener; @@ -1160,14 +1156,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, ConfigurationContainer requestingContainer) { - final int previousRotation = mRotation; final Configuration config = updateOrientationFromAppTokens( getRequestedOverrideConfiguration(), freezeDisplayToken, false); - // This event is considered handled iff a configuration propagation is triggered, because - // that's the only place lower level containers check if they need to do something to this - // request. The only guaranteed signal is that the display is rotated to a different - // orientation (i.e. rotating 180 degrees doesn't count). - final boolean handled = (mRotation - previousRotation) % 2 != 0; + // If display rotation class tells us that it doesn't consider app requested orientation, + // this display won't rotate just because of an app changes its requested orientation. Thus + // it indicates that this display chooses not to handle this request. + final boolean handled = getDisplayRotation().respectAppRequestedOrientation(); if (config == null) { return handled; } @@ -1189,6 +1183,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return handled; } + @Override + boolean handlesOrientationChangeFromDescendant() { + return getDisplayRotation().respectAppRequestedOrientation(); + } + /** * Determine the new desired orientation of this display. * @@ -1369,8 +1368,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId + " selected orientation " + lastOrientation - + ", got rotation " + rotation + " which has " - + " metrics"); + + ", got rotation " + rotation); if (oldRotation == rotation) { // No change. diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index bc165dceb544..5f341ee8002c 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -329,6 +329,15 @@ public class DisplayRotation { return mFixedToUserRotation; } + /** + * Returns {@code true} if this display rotation takes app requested orientation into + * consideration; {@code false} otherwise. For the time being the only case where this is {@code + * false} is when {@link #isFixedToUserRotation()} is {@code true}. + */ + boolean respectAppRequestedOrientation() { + return !mFixedToUserRotation; + } + public int getLandscapeRotation() { return mLandscapeRotation; } diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index af38c0680c51..69f0012b69ba 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -2197,11 +2197,15 @@ class TaskRecord extends ConfigurationContainer { // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent" outOverrideBounds.setEmpty(); + final boolean parentHandlesOrientationChange = mTask != null + && mTask.getParent() != null + && mTask.getParent().handlesOrientationChangeFromDescendant(); // If the task or its top activity requires a different orientation, make it fit the // available bounds by scaling down its bounds. int forcedOrientation = getTopActivityRequestedOrientation(); if (forcedOrientation != ORIENTATION_UNDEFINED - && forcedOrientation != newParentConfig.orientation) { + && forcedOrientation != newParentConfig.orientation + && !parentHandlesOrientationChange) { final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); final int parentWidth = parentBounds.width(); final int parentHeight = parentBounds.height(); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index feac6c6f0daf..628c8add3f93 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -714,6 +714,21 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } /** + * Check if this container or its parent will handle orientation changes from descendants. It's + * different from the return value of {@link #onDescendantOrientationChanged(IBinder, + * ConfigurationContainer)} in the sense that the return value of this method tells if this + * container or its parent will handle the request eventually, while the return value of the + * other method is if it handled the request synchronously. + * + * @return {@code true} if it handles or will handle orientation change in the future; {@code + * false} if it won't handle the change at anytime. + */ + boolean handlesOrientationChangeFromDescendant() { + final WindowContainer parent = getParent(); + return parent != null && parent.handlesOrientationChangeFromDescendant(); + } + + /** * Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2 * parameters. * 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 f3994630adee..3826fac22b05 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -59,7 +59,6 @@ import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.view.DisplayCutout; -import android.view.DisplayInfo; import android.view.Gravity; import android.view.MotionEvent; import android.view.Surface; @@ -585,17 +584,15 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testOnDescendantOrientationRequestChanged() { - final DisplayInfo info = new DisplayInfo(); - info.logicalWidth = 1080; - info.logicalHeight = 1920; - info.logicalDensityDpi = 240; - final DisplayContent dc = createNewDisplay(info); - dc.configureDisplayPolicy(); + final DisplayContent dc = createNewDisplay(); mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class); + final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE + ? SCREEN_ORIENTATION_PORTRAIT + : SCREEN_ORIENTATION_LANDSCAPE; final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS); - window.mAppToken.mOrientation = SCREEN_ORIENTATION_LANDSCAPE; + window.mAppToken.setOrientation(newOrientation); ActivityRecord activityRecord = mock(ActivityRecord.class); @@ -606,22 +603,21 @@ public class DisplayContentTests extends WindowTestsBase { verify(mWm.mAtmService).updateDisplayOverrideConfigurationLocked(captor.capture(), same(activityRecord), anyBoolean(), eq(dc.getDisplayId())); final Configuration newDisplayConfig = captor.getValue(); - assertEquals(Configuration.ORIENTATION_LANDSCAPE, newDisplayConfig.orientation); + assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation); } @Test public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() { - final DisplayInfo info = new DisplayInfo(); - info.logicalWidth = 1080; - info.logicalHeight = 1920; - info.logicalDensityDpi = 240; - final DisplayContent dc = createNewDisplay(info); + final DisplayContent dc = createNewDisplay(); dc.getDisplayRotation().setFixedToUserRotation(true); mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class); + final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE + ? SCREEN_ORIENTATION_PORTRAIT + : SCREEN_ORIENTATION_LANDSCAPE; final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS); - window.mAppToken.mOrientation = SCREEN_ORIENTATION_LANDSCAPE; + window.mAppToken.setOrientation(newOrientation); ActivityRecord activityRecord = mock(ActivityRecord.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index d05711e914c0..198e7ce63f52 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -33,6 +33,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.content.ContentResolver; @@ -611,6 +612,23 @@ public class DisplayRotationTests { // ======================== // Non-rotation API Tests // ======================== + @Test + public void testRespectsAppRequestedOrientationByDefault() throws Exception { + mBuilder.build(); + + assertTrue("Display rotation should respect app requested orientation by" + + " default.", mTarget.respectAppRequestedOrientation()); + } + + @Test + public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception { + mBuilder.build(); + mTarget.setFixedToUserRotation(true); + + assertFalse("Display rotation shouldn't respect app requested orientation if" + + " fixed to user rotation.", mTarget.respectAppRequestedOrientation()); + } + /** * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget} * according to given parameters. diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index e182c45f009e..bcf9dd218835 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -25,13 +25,6 @@ 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; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.hamcrest.Matchers.not; @@ -41,6 +34,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; import android.app.ActivityManager; import android.content.ComponentName; @@ -53,7 +47,6 @@ import android.platform.test.annotations.Presubmit; import android.service.voice.IVoiceInteractionSession; import android.util.Xml; import android.view.DisplayInfo; -import android.view.Surface; import androidx.test.filters.MediumTest; @@ -62,6 +55,7 @@ import com.android.server.wm.TaskRecord.TaskRecordFactory; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -284,48 +278,33 @@ public class TaskRecordTests extends ActivityTestsBase { } @Test - public void testUpdatesForcedOrientationInBackground() { - final DisplayInfo info = new DisplayInfo(); - info.logicalWidth = 1920; - info.logicalHeight = 1080; - final ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP); - doCallRealMethod().when(display.mDisplayContent).setDisplayRotation(any()); - display.mDisplayContent.setDisplayRotation(mock(DisplayRotation.class)); - doCallRealMethod().when(display.mDisplayContent).onDescendantOrientationChanged(any(), - any()); - doCallRealMethod().when(display.mDisplayContent).setRotation(anyInt()); - doAnswer(invocation -> { - display.mDisplayContent.setRotation(Surface.ROTATION_0); - return null; - }).when(display.mDisplayContent).updateOrientationFromAppTokens(any(), any(), anyBoolean()); + public void testIgnoresForcedOrientationWhenParentHandles() { + final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); + DisplayInfo info = new DisplayInfo(); + info.logicalWidth = fullScreenBounds.width(); + info.logicalHeight = fullScreenBounds.height(); + ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP); - final ActivityStack stack = new StackBuilder(mRootActivityContainer) + display.getRequestedOverrideConfiguration().orientation = + Configuration.ORIENTATION_LANDSCAPE; + display.onRequestedOverrideConfigurationChanged( + display.getRequestedOverrideConfiguration()); + ActivityStack stack = new StackBuilder(mRootActivityContainer) .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); - final TaskRecord task = stack.getChildAt(0); - final ActivityRecord activity = task.getRootActivity(); - - // Wire up app window token and task. - doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt(), any(), any()); - doCallRealMethod().when(activity.mAppWindowToken).onDescendantOrientationChanged(any(), - any()); - doReturn(task.mTask).when(activity.mAppWindowToken).getParent(); - - // Wire up task and stack. - task.mTask.mTaskRecord = task; - doCallRealMethod().when(task.mTask).onDescendantOrientationChanged(any(), any()); - doReturn(stack.getTaskStack()).when(task.mTask).getParent(); - - // Wire up stack and display content. - doCallRealMethod().when(stack.mTaskStack).onDescendantOrientationChanged(any(), any()); - doReturn(display.mDisplayContent).when(stack.mTaskStack).getParent(); - - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - assertTrue("Bounds of the task should be pillarboxed.", - task.getBounds().width() < task.getBounds().height()); - - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); - assertTrue("Bounds of the task should be fullscreen.", - task.getBounds().equals(new Rect(0, 0, 1920, 1080))); + TaskRecord task = stack.getChildAt(0); + ActivityRecord root = task.getTopActivity(); + + final WindowContainer parentWindowContainer = mock(WindowContainer.class); + Mockito.doReturn(parentWindowContainer).when(task.mTask).getParent(); + Mockito.doReturn(true).when(parentWindowContainer) + .handlesOrientationChangeFromDescendant(); + + // Setting app to fixed portrait fits within parent, but TaskRecord shouldn't adjust the + // bounds because its parent says it will handle it at a later time. + setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT); + assertEquals(root, task.getRootActivity()); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation()); + assertEquals(fullScreenBounds, task.getBounds()); } /** Ensures that the alias intent won't have target component resolved. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index 862857560a98..a9a76c2e9506 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -51,6 +51,7 @@ import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Test; +import org.mockito.Mockito; import java.util.Comparator; @@ -739,6 +740,18 @@ public class WindowContainerTests extends WindowTestsBase { verify(root).onDescendantOrientationChanged(binder, activityRecord); } + @Test + public void testHandlesOrientationChangeFromDescendantProgation() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = spy(builder.build()); + + final TestWindowContainer child = root.addChildWindow(); + assertFalse(child.handlesOrientationChangeFromDescendant()); + + Mockito.doReturn(true).when(root).handlesOrientationChangeFromDescendant(); + assertTrue(child.handlesOrientationChangeFromDescendant()); + } + /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { private final int mLayer; |