From d5f2cd95f20f8deb12c9b99b4d08d6a4b39cb10f Mon Sep 17 00:00:00 2001 From: Chris Li Date: Fri, 23 Oct 2020 15:32:54 -0700 Subject: [PATCH] Clear the previous CompatDisplayInsets when new activity is launched When a new activity is launched from Task letterbox activity, it will first use the existing Task bounds, so we need to make sure the size compat for that bounds is cleared when it requests orientation. Bug: 170725334 Bug: 155431879 Test: atest WmTests:SizeCompatTests Test: manual: adb shell wm set-ignore-orientation-request true Change-Id: I86a206be030c3c250fb40590e7515e75620eaaf7 --- .../com/android/server/wm/ActivityRecord.java | 14 ++- .../com/android/server/wm/DisplayContent.java | 2 +- .../core/java/com/android/server/wm/Task.java | 14 ++- .../android/server/wm/WindowContainer.java | 6 +- .../android/server/wm/SizeCompatTests.java | 92 ++++++++++++++++++- 5 files changed, 118 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b40154e8db0ca..d9faaf868b3a2 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -6543,14 +6543,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mCompatDisplayInsets = new CompatDisplayInsets(mDisplayContent, this); } - @VisibleForTesting - void clearSizeCompatMode() { + void clearSizeCompatMode(boolean recomputeTask) { mSizeCompatScale = 1f; mSizeCompatBounds = null; mCompatDisplayInsets = null; - // Recompute from Task because letterbox can also happen on Task level. - task.onRequestedOverrideConfigurationChanged(task.getRequestedOverrideConfiguration()); + if (recomputeTask) { + // Recompute from Task because letterbox can also happen on Task level. + task.onRequestedOverrideConfigurationChanged(task.getRequestedOverrideConfiguration()); + } + } + + @VisibleForTesting + void clearSizeCompatMode() { + clearSizeCompatMode(true /* recomputeTask */); } @Override diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 98a1bacf27e02..e8a4dc3814d03 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1303,7 +1303,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @Override boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, - ConfigurationContainer requestingContainer) { + WindowContainer requestingContainer) { final Configuration config = updateOrientation( getRequestedOverrideConfiguration(), freezeDisplayToken, false /* forceUpdate */); // If display rotation class tells us that it doesn't consider app requested orientation, diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 4b12fe1c5e384..d423766e94305 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3274,7 +3274,7 @@ class Task extends WindowContainer { @Override public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, - ConfigurationContainer requestingContainer) { + WindowContainer requestingContainer) { if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) { return true; } @@ -3282,6 +3282,18 @@ class Task extends WindowContainer { // No one in higher hierarchy handles this request, let's adjust our bounds to fulfill // it if possible. if (getParent() != null) { + final ActivityRecord activity = requestingContainer.asActivityRecord(); + if (activity != null) { + // Clear the size compat cache to recompute the bounds for requested orientation; + // otherwise when Task#computeFullscreenBounds(), it will not try to do Task level + // letterboxing because app may prefer to keep its original size (size compat). + // + // Normally, ActivityRecord#clearSizeCompatMode() recomputes from its parent Task, + // which is the leaf Task. However, because this orientation request is new to all + // Tasks, pass false to clearSizeCompatMode, and trigger onConfigurationChanged from + // here (root Task) to make sure all Tasks are up-to-date. + activity.clearSizeCompatMode(false /* recomputeTask */); + } onConfigurationChanged(getParent().getConfiguration()); return true; } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index da3a92856fe58..cec2b73355546 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1144,7 +1144,7 @@ class WindowContainer extends ConfigurationContainer< * @return {@code true} if handled; {@code false} otherwise. */ boolean onDescendantOrientationChanged(@Nullable IBinder freezeDisplayToken, - @Nullable ConfigurationContainer requestingContainer) { + @Nullable WindowContainer requestingContainer) { final WindowContainer parent = getParent(); if (parent == null) { return false; @@ -1156,7 +1156,7 @@ class 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 + * WindowContainer)} 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. * @@ -1230,7 +1230,7 @@ class WindowContainer extends ConfigurationContainer< * to ensure it gets correct configuration. */ void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken, - @Nullable ConfigurationContainer requestingContainer) { + @Nullable WindowContainer requestingContainer) { if (mOrientation == orientation) { return; } diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 7a1b17dfead7c..b7f6564b4135e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -28,6 +28,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.Task.ActivityState.STOPPED; @@ -35,8 +36,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.doCallRealMethod; import android.app.ActivityManager; @@ -628,7 +632,6 @@ public class SizeCompatTests extends WindowTestsBase { rotateDisplay(mActivity.mDisplayContent, ROTATION_90); final Rect displayBounds = mActivity.mDisplayContent.getBounds(); - final Rect newTaskBounds = mTask.getBounds(); final Rect newActivityBounds = mActivity.getBounds(); assertTrue(displayBounds.width() < displayBounds.height()); @@ -673,6 +676,93 @@ public class SizeCompatTests extends WindowTestsBase { activityBounds.width()); } + @Test + public void testDisplayIgnoreOrientationRequest_newLaunchedOrientationAppInTaskLetterbox() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(2800, 1400); + final DisplayContent display = mActivity.mDisplayContent; + display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + // Portrait fixed app without max aspect. + prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT); + + assertTrue(mTask.isTaskLetterboxed()); + assertFalse(mActivity.inSizeCompatMode()); + + // Launch another portrait fixed app. + spyOn(mTask); + setBooted(display.mWmService.mAtmService); + final ActivityRecord newActivity = new ActivityBuilder(display.mWmService.mAtmService) + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .setTask(mTask) + .build(); + + // Update with new activity requested orientation and recompute bounds with no previous + // size compat cache. + verify(mTask).onDescendantOrientationChanged(any(), same(newActivity)); + verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt()); + verify(newActivity).clearSizeCompatMode(false /* recomputeTask */); + + final Rect displayBounds = display.getBounds(); + final Rect taskBounds = mTask.getBounds(); + final Rect newActivityBounds = newActivity.getBounds(); + + // Task and app bounds should be 700x1400 with the ratio as the display. + assertTrue(mTask.isTaskLetterboxed()); + assertFalse(newActivity.inSizeCompatMode()); + assertEquals(taskBounds, newActivityBounds); + assertEquals(displayBounds.height(), taskBounds.height()); + assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(), + taskBounds.width()); + } + + @Test + public void testDisplayIgnoreOrientationRequest_newLaunchedMaxAspectApp() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(2800, 1400); + final DisplayContent display = mActivity.mDisplayContent; + display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + // Portrait fixed app without max aspect. + prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT); + + assertTrue(mTask.isTaskLetterboxed()); + assertFalse(mActivity.inSizeCompatMode()); + + // Launch another portrait fixed app with max aspect ratio as 1.3. + spyOn(mTask); + setBooted(display.mWmService.mAtmService); + final ActivityRecord newActivity = new ActivityBuilder(display.mWmService.mAtmService) + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .setMaxAspectRatio(1.3f) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .setTask(mTask) + .build(); + + // Update with new activity requested orientation and recompute bounds with no previous + // size compat cache. + verify(mTask).onDescendantOrientationChanged(any(), same(newActivity)); + verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt()); + verify(newActivity).clearSizeCompatMode(false /* recomputeTask */); + + final Rect displayBounds = display.getBounds(); + final Rect taskBounds = mTask.getBounds(); + final Rect newActivityBounds = newActivity.getBounds(); + + // Task bounds should be 700x1400 with the ratio as the display. + assertTrue(mTask.isTaskLetterboxed()); + assertEquals(displayBounds.height(), taskBounds.height()); + assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(), + taskBounds.width()); + + // App bounds should be 700x(710 x 1.3 = 910) + assertFalse(newActivity.inSizeCompatMode()); + assertEquals(taskBounds.width(), newActivityBounds.width()); + assertEquals((long) Math.rint(taskBounds.width() * newActivity.info.maxAspectRatio), + newActivityBounds.height()); + } + private static WindowState addWindowToActivity(ActivityRecord activity) { final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;