From bbe25f59ba77f3dd3b86938a681b8d266198eb1e Mon Sep 17 00:00:00 2001 From: Chris Li Date: Wed, 9 Sep 2020 12:31:27 -0700 Subject: [PATCH] Only allow TaskDisplayArea to specify orientation if it is focused (1/n Orientation on DisplayAreaGroup) Record the last focused TDA and only allow it to specify orientation if it is the last focused one on the logical display, so that when there are multiple TDAs, we only respect the TDA with focus. Bug: 155431879 Test: manual: test with fixed-orientation app Test: atest WmTests:TaskRecordTests Test: atest WmTests:TaskDisplayAreaTests Change-Id: Ia574550368ce45a6e2c2f81b543adcfa4cf01ba1 --- data/etc/services.core.protolog.json | 24 ++++++------ .../com/android/server/wm/DisplayContent.java | 38 +++++++++++++++++- .../android/server/wm/TaskDisplayArea.java | 30 +++++++------- .../server/wm/SystemServicesTestRule.java | 3 ++ .../server/wm/TaskDisplayAreaTests.java | 35 +++++++++++++++++ .../android/server/wm/TaskRecordTests.java | 39 ++++++++++++++++++- .../android/server/wm/TestDisplayContent.java | 4 ++ 7 files changed, 142 insertions(+), 31 deletions(-) diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index e69319896762e..06c3276bbc1d5 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -253,6 +253,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-1741065110": { + "message": "No app is requesting an orientation, return %d for display id=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayContent.java" + }, "-1730156332": { "message": "Display id=%d rotation changed to %d from %d, lastOrientation=%d", "level": "VERBOSE", @@ -1897,6 +1903,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowToken.java" }, + "845234215": { + "message": "App is requesting an orientation, return %d for display id=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayContent.java" + }, "853091290": { "message": "Moved stack=%s behind stack=%s", "level": "DEBUG", @@ -2167,12 +2179,6 @@ "group": "WM_DEBUG_IME", "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java" }, - "1381227466": { - "message": "App is requesting an orientation, return %d for display id=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/TaskDisplayArea.java" - }, "1401295262": { "message": "Mode default, asking user", "level": "WARN", @@ -2359,12 +2365,6 @@ "group": "WM_DEBUG_RESIZE", "at": "com\/android\/server\/wm\/WindowState.java" }, - "1640436199": { - "message": "No app is requesting an orientation, return %d for display id=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/TaskDisplayArea.java" - }, "1653210583": { "message": "Removing app %s delayed=%b animation=%s animating=%b", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 4db121b6ea335..75116444a6b1b 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -28,6 +28,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; @@ -465,6 +467,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ ActivityRecord mFocusedApp = null; + /** The last focused {@link TaskDisplayArea} on this display. */ + private TaskDisplayArea mLastFocusedTaskDisplayArea = null; + /** * The launching activity which is using fixed rotation transformation. * @@ -2328,7 +2333,21 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return getLastOrientation(); } } - return super.getOrientation(); + + final int orientation = super.getOrientation(); + if (orientation != SCREEN_ORIENTATION_UNSET && orientation != SCREEN_ORIENTATION_BEHIND) { + ProtoLog.v(WM_DEBUG_ORIENTATION, + "App is requesting an orientation, return %d for display id=%d", + orientation, mDisplayId); + return orientation; + } + + ProtoLog.v(WM_DEBUG_ORIENTATION, + "No app is requesting an orientation, return %d for display id=%d", + getLastOrientation(), mDisplayId); + // The next app has not been requested to be visible, so we keep the current orientation + // to prevent freezing/unfreezing the display too early. + return getLastOrientation(); } void updateDisplayInfo() { @@ -3203,6 +3222,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp throw new IllegalStateException(newFocus + " is not on " + getName() + " but " + ((appDisplay != null) ? appDisplay.getName() : "none")); } + + // Called even if the focused app is not changed in case the app is moved to a different + // TaskDisplayArea. + setLastFocusedTaskDisplayArea(newFocus.getDisplayArea()); } if (mFocusedApp == newFocus) { return false; @@ -3215,6 +3238,19 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return true; } + /** Called when the focused {@link TaskDisplayArea} on this display may have changed. */ + @VisibleForTesting + void setLastFocusedTaskDisplayArea(@Nullable TaskDisplayArea taskDisplayArea) { + if (taskDisplayArea != null) { + mLastFocusedTaskDisplayArea = taskDisplayArea; + } + } + + /** Gets the last focused {@link TaskDisplayArea} on this display. */ + TaskDisplayArea getLastFocusedTaskDisplayArea() { + return mLastFocusedTaskDisplayArea; + } + /** Updates the layer assignment of windows on this display. */ void assignWindowLayers(boolean setLayoutNeeded) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers"); diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 890f56e50bacc..9946fe08623e9 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -29,12 +29,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.isSplitScreenWindowingMode; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; -import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS; @@ -645,6 +643,12 @@ final class TaskDisplayArea extends DisplayArea { @Override int getOrientation(int candidate) { + // Only allow to specify orientation if this TDA has the focus. + // TODO(b/155431879) Add option to never allow a TDA to specify orientation. + if (!isLastFocused()) { + return SCREEN_ORIENTATION_UNSET; + } + if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { // Apps and their containers are not allowed to specify an orientation while using // root tasks...except for the home stack if it is not resizable and currently @@ -671,21 +675,7 @@ final class TaskDisplayArea extends DisplayArea { return SCREEN_ORIENTATION_UNSPECIFIED; } - final int orientation = super.getOrientation(candidate); - if (orientation != SCREEN_ORIENTATION_UNSET - && orientation != SCREEN_ORIENTATION_BEHIND) { - ProtoLog.v(WM_DEBUG_ORIENTATION, - "App is requesting an orientation, return %d for display id=%d", - orientation, mDisplayContent.mDisplayId); - return orientation; - } - - ProtoLog.v(WM_DEBUG_ORIENTATION, - "No app is requesting an orientation, return %d for display id=%d", - mDisplayContent.getLastOrientation(), mDisplayContent.mDisplayId); - // The next app has not been requested to be visible, so we keep the current orientation - // to prevent freezing/unfreezing the display too early. - return mDisplayContent.getLastOrientation(); + return super.getOrientation(candidate); } @Override @@ -1880,6 +1870,12 @@ final class TaskDisplayArea extends DisplayArea { return lastReparentedStack; } + /** Whether this task display area is the last focused one on this logical display. */ + @VisibleForTesting + boolean isLastFocused() { + return mDisplayContent.getLastFocusedTaskDisplayArea() == this; + } + @Override protected boolean isTaskDisplayArea() { return true; diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 6a29c5b5424a2..da3319acca24f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -318,6 +318,9 @@ public class SystemServicesTestRule implements TestRule { display.setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN); spyOn(display); final TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); + + // Set the default focused TDA. + display.setLastFocusedTaskDisplayArea(taskDisplayArea); spyOn(taskDisplayArea); final Task homeStack = taskDisplayArea.getStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index bc3b3a4d7ad1f..e71f7ec54b0e7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -29,6 +29,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; @@ -242,6 +243,40 @@ public class TaskDisplayAreaTests extends WindowTestsBase { assertEquals(rootWindowContainer.getOrientation(), rootHomeTask.getOrientation()); } + @Test + public void testIsLastFocused() { + final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( + mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", + FEATURE_VENDOR_FIRST); + final Task firstStack = firstTaskDisplayArea.createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final Task secondStack = secondTaskDisplayArea.createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setCreateTask(true) + .setStack(firstStack).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setCreateTask(true) + .setStack(secondStack).build(); + + // Activity on TDA1 is focused + mDisplayContent.setFocusedApp(firstActivity); + + assertThat(firstTaskDisplayArea.isLastFocused()).isTrue(); + assertThat(secondTaskDisplayArea.isLastFocused()).isFalse(); + + // No focused app, TDA1 is still recorded as last focused. + mDisplayContent.setFocusedApp(null); + + assertThat(firstTaskDisplayArea.isLastFocused()).isTrue(); + assertThat(secondTaskDisplayArea.isLastFocused()).isFalse(); + + // Activity on TDA2 is focused + mDisplayContent.setFocusedApp(secondActivity); + + assertThat(firstTaskDisplayArea.isLastFocused()).isFalse(); + assertThat(secondTaskDisplayArea.isLastFocused()).isTrue(); + } + private void assertGetOrCreateStack(int windowingMode, int activityType, Task candidateTask, boolean reuseCandidate) { final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea(); 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 a908bfef98de1..d2b7ac4c3b248 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -33,6 +33,7 @@ import static android.util.DisplayMetrics.DENSITY_DEFAULT; import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_90; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -1001,7 +1002,7 @@ public class TaskRecordTests extends WindowTestsBase { public void testNotSpecifyOrientationByFloatingTask() { final Task task = getTestTask(); final ActivityRecord activity = task.getTopMostActivity(); - final WindowContainer taskDisplayArea = task.getParent(); + final TaskDisplayArea taskDisplayArea = task.getDisplayArea(); activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation()); @@ -1011,6 +1012,42 @@ public class TaskRecordTests extends WindowTestsBase { assertEquals(SCREEN_ORIENTATION_UNSET, taskDisplayArea.getOrientation()); } + @Test + public void testNotSpecifyOrientation_taskDisplayAreaNotFocused() { + final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( + mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", + FEATURE_VENDOR_FIRST); + final Task firstStack = firstTaskDisplayArea.createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final Task secondStack = secondTaskDisplayArea.createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setCreateTask(true) + .setStack(firstStack).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setCreateTask(true) + .setStack(secondStack).build(); + firstActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + secondActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + + // Activity on TDA1 is focused + mDisplayContent.setFocusedApp(firstActivity); + + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation()); + assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation()); + + // No focused app, TDA1 is still recorded as last focused. + mDisplayContent.setFocusedApp(null); + + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation()); + assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation()); + + // Activity on TDA2 is focused + mDisplayContent.setFocusedApp(secondActivity); + + assertEquals(SCREEN_ORIENTATION_UNSET, firstTaskDisplayArea.getOrientation()); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, secondTaskDisplayArea.getOrientation()); + } + @Test public void testNotifyOrientationChangeCausedByConfigurationChange() { final Task task = getTestTask(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java index 213c1f52aa378..ee16a76bcff8c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java @@ -156,6 +156,10 @@ class TestDisplayContent extends DisplayContent { // threads immediately after adding it to hierarchy. Calling doAnswer() type of stubbing // reduces chance of races, but still doesn't eliminate race conditions. mService.mRootWindowContainer.addChild(newDisplay, mPosition); + + // Set the default focused TDA. + newDisplay.setLastFocusedTaskDisplayArea(newDisplay.getDefaultTaskDisplayArea()); + return newDisplay; } }