From cc2d539e801ab7aa748c780c41c1a1569bdd112c Mon Sep 17 00:00:00 2001 From: Massimo Carli Date: Wed, 23 Nov 2022 13:48:01 +0000 Subject: [PATCH] Respect "nosensor" and "locked" with ignoreOrientationRequest being set In this implementation, we always resolve the orientation for a component as the ignoreOrientationRequest was disabled. We then use the actual flag when deciding the display orientation and when querying from a descendent to handle the orientation change itself. Main use case why this is important is Camera apps that rely on those properties to ensure that they will be able to determine Camera preview orientation correctly Fixes:254691921 Test: run `atest WmTests:ActivityRecordTests WmTests:TaskTests WmTests:WindowContainerTests` Change-Id: I96dca18a561e2acf961a462d3a4db484f003be6f Merged-In: I96dca18a561e2acf961a462d3a4db484f003be6f --- data/etc/services.core.protolog.json | 12 +- .../com/android/server/wm/ActivityRecord.java | 7 +- .../com/android/server/wm/DisplayArea.java | 45 ++++++-- .../com/android/server/wm/DisplayContent.java | 53 ++++----- .../core/java/com/android/server/wm/Task.java | 7 +- .../android/server/wm/TaskDisplayArea.java | 30 +++-- .../android/server/wm/WindowContainer.java | 7 +- .../server/wm/ActivityRecordTests.java | 2 +- .../android/server/wm/DisplayAreaTest.java | 70 ++++++++---- .../wm/DualDisplayAreaGroupPolicyTest.java | 75 +++++++++++++ .../server/wm/TaskDisplayAreaTests.java | 105 ++++++++++++++---- .../src/com/android/server/wm/TaskTests.java | 14 ++- .../server/wm/WindowContainerTests.java | 15 ++- 13 files changed, 306 insertions(+), 136 deletions(-) diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index a4a38c713a66c..86c8097ee6743 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -3871,12 +3871,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/TaskDisplayArea.java" }, - "1648338379": { - "message": "Display id=%d is ignoring all orientation requests, return %d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, "1653025361": { "message": "Register task fragment organizer=%s uid=%d pid=%d", "level": "VERBOSE", @@ -4117,6 +4111,12 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/DisplayAreaPolicyBuilder.java" }, + "1877863087": { + "message": "Display id=%d is ignoring orientation request for %d, return %d", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayContent.java" + }, "1878927091": { "message": "prepareSurface: No changes in animation for %s", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e639866a6babe..418e1edca05b4 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -7756,10 +7756,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // configuration. This is important to cases where activities with incompatible // orientations launch, or user goes back from an activity of bi-orientation to an // activity with specified orientation. - if (getRequestedOrientation() == SCREEN_ORIENTATION_UNSET) { - return; - } - if (onDescendantOrientationChanged(this)) { // WM Shell can show additional UI elements, e.g. a restart button for size compat mode // so ensure that WM Shell is called when an activity becomes visible. @@ -8329,7 +8325,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // If orientation is respected when insets are applied, then stableBounds will be empty. boolean orientationRespectedWithInsets = orientationRespectedWithInsets(parentBounds, stableBounds); - if (handlesOrientationChangeFromDescendant() && orientationRespectedWithInsets) { + if (orientationRespectedWithInsets + && handlesOrientationChangeFromDescendant(mOrientation)) { // No need to letterbox because of fixed orientation. Display will handle // fixed-orientation requests and a display rotation is enough to respect requested // orientation with insets applied. diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index a32e46078d82e..af5bd14baf312 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -34,6 +34,8 @@ import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA; import android.annotation.Nullable; +import android.content.pm.ActivityInfo; +import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.res.Configuration; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; @@ -141,26 +143,30 @@ public class DisplayArea extends WindowContainer { } @Override + @ScreenOrientation int getOrientation(int candidate) { - mLastOrientationSource = null; - if (getIgnoreOrientationRequest()) { + final int orientation = super.getOrientation(candidate); + if (getIgnoreOrientationRequest(orientation)) { + // In all the other case, mLastOrientationSource will be reassigned to a new value + mLastOrientationSource = null; return SCREEN_ORIENTATION_UNSET; } - - return super.getOrientation(candidate); + return orientation; } @Override - boolean handlesOrientationChangeFromDescendant() { - return !getIgnoreOrientationRequest() - && super.handlesOrientationChangeFromDescendant(); + boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) { + return !getIgnoreOrientationRequest(orientation) + && super.handlesOrientationChangeFromDescendant(orientation); } @Override - boolean onDescendantOrientationChanged(WindowContainer requestingContainer) { + boolean onDescendantOrientationChanged(@Nullable WindowContainer requestingContainer) { // If this is set to ignore the orientation request, we don't propagate descendant // orientation request. - return !getIgnoreOrientationRequest() + final int orientation = requestingContainer != null + ? requestingContainer.mOrientation : SCREEN_ORIENTATION_UNSET; + return !getIgnoreOrientationRequest(orientation) && super.onDescendantOrientationChanged(requestingContainer); } @@ -224,6 +230,23 @@ public class DisplayArea extends WindowContainer { } } + /** + * @return {@value true} if we need to ignore the orientation in input. + */ + // TODO(b/262366204): Rename getIgnoreOrientationRequest to shouldIgnoreOrientationRequest + boolean getIgnoreOrientationRequest(@ScreenOrientation int orientation) { + // We always respect orientation request for ActivityInfo.SCREEN_ORIENTATION_LOCKED + // ActivityInfo.SCREEN_ORIENTATION_NOSENSOR. + // Main use case why this is important is Camera apps that rely on those + // properties to ensure that they will be able to determine Camera preview + // orientation correctly + if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED + || orientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { + return false; + } + return getIgnoreOrientationRequest(); + } + boolean getIgnoreOrientationRequest() { // Adding an exception for when ignoreOrientationRequest is overridden at runtime for all // DisplayArea-s. For example, this is needed for the Kids Mode since many Kids apps aren't @@ -640,11 +663,9 @@ public class DisplayArea extends WindowContainer { } @Override + @ScreenOrientation int getOrientation(int candidate) { mLastOrientationSource = null; - if (getIgnoreOrientationRequest()) { - return SCREEN_ORIENTATION_UNSET; - } // Find a window requesting orientation. final WindowState win = getWindow(mGetOrientingWindow); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 5d502f5420ca6..4c499861cb9aa 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1558,13 +1558,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } @Override - boolean onDescendantOrientationChanged(WindowContainer requestingContainer) { + boolean onDescendantOrientationChanged(@Nullable WindowContainer requestingContainer) { final Configuration config = updateOrientation( requestingContainer, false /* forceUpdate */); // 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 = handlesOrientationChangeFromDescendant(); + final int orientation = requestingContainer != null ? requestingContainer.mOrientation + : SCREEN_ORIENTATION_UNSET; + final boolean handled = handlesOrientationChangeFromDescendant(orientation); if (config == null) { return handled; } @@ -1587,8 +1589,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } @Override - boolean handlesOrientationChangeFromDescendant() { - return !getIgnoreOrientationRequest() + boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) { + return !getIgnoreOrientationRequest(orientation) && !getDisplayRotation().isFixedToUserRotation(); } @@ -1689,7 +1691,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return ROTATION_UNDEFINED; } if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM - || getIgnoreOrientationRequest()) { + || getIgnoreOrientationRequest(r.mOrientation)) { return ROTATION_UNDEFINED; } if (r.mOrientation == ActivityInfo.SCREEN_ORIENTATION_BEHIND) { @@ -2688,15 +2690,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @ScreenOrientation @Override int getOrientation() { - mLastOrientationSource = null; - if (!handlesOrientationChangeFromDescendant()) { - // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation - ProtoLog.v(WM_DEBUG_ORIENTATION, - "Display id=%d is ignoring all orientation requests, return %d", - mDisplayId, SCREEN_ORIENTATION_UNSPECIFIED); - return SCREEN_ORIENTATION_UNSPECIFIED; - } - if (mWmService.mDisplayFrozen) { if (mWmService.mPolicy.isKeyguardLocked()) { // Use the last orientation the while the display is frozen with the keyguard @@ -2712,6 +2705,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } final int orientation = super.getOrientation(); + + if (!handlesOrientationChangeFromDescendant(orientation)) { + mLastOrientationSource = null; + // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation + ProtoLog.v(WM_DEBUG_ORIENTATION, + "Display id=%d is ignoring orientation request for %d, return %d", + mDisplayId, orientation, SCREEN_ORIENTATION_UNSPECIFIED); + return SCREEN_ORIENTATION_UNSPECIFIED; + } + if (orientation == SCREEN_ORIENTATION_UNSET) { // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation ProtoLog.v(WM_DEBUG_ORIENTATION, @@ -3832,18 +3835,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** Called when the focused {@link TaskDisplayArea} on this display may have changed. */ void onLastFocusedTaskDisplayAreaChanged(@Nullable TaskDisplayArea taskDisplayArea) { - // Only record the TaskDisplayArea that handles orientation request. - if (taskDisplayArea != null && taskDisplayArea.handlesOrientationChangeFromDescendant()) { - mOrientationRequestingTaskDisplayArea = taskDisplayArea; - return; - } - - // If the previous TDA no longer handles orientation request, clear it. - if (mOrientationRequestingTaskDisplayArea != null - && !mOrientationRequestingTaskDisplayArea - .handlesOrientationChangeFromDescendant()) { - mOrientationRequestingTaskDisplayArea = null; - } + mOrientationRequestingTaskDisplayArea = taskDisplayArea; } /** @@ -5053,13 +5045,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } @Override - int getOrientation(int candidate) { - if (getIgnoreOrientationRequest()) { - return SCREEN_ORIENTATION_UNSET; - } - + @ScreenOrientation + int getOrientation(@ScreenOrientation int candidate) { // IME does not participate in orientation. - return candidate; + return getIgnoreOrientationRequest(candidate) ? SCREEN_ORIENTATION_UNSET : candidate; } @Override diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 292ed951b7e7b..7ba07319b8703 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -143,6 +143,7 @@ import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; @@ -2686,8 +2687,8 @@ class Task extends TaskFragment { } @Override - boolean handlesOrientationChangeFromDescendant() { - if (!super.handlesOrientationChangeFromDescendant()) { + boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) { + if (!super.handlesOrientationChangeFromDescendant(orientation)) { return false; } @@ -2702,7 +2703,7 @@ class Task extends TaskFragment { // Check for leaf Task. // Display won't rotate for the orientation request if the Task/TaskDisplayArea // can't specify orientation. - return canSpecifyOrientation() && getDisplayArea().canSpecifyOrientation(); + return canSpecifyOrientation() && getDisplayArea().canSpecifyOrientation(orientation); } void resize(boolean relayout, boolean forced) { diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index e0ed356fbe591..8ad76a3eb3271 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -41,6 +41,7 @@ import android.annotation.Nullable; import android.app.ActivityOptions; import android.app.WindowConfiguration; import android.content.pm.ActivityInfo; +import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.res.Configuration; import android.graphics.Color; import android.os.UserHandle; @@ -633,22 +634,20 @@ final class TaskDisplayArea extends DisplayArea { } @Override - int getOrientation(int candidate) { - mLastOrientationSource = null; - if (getIgnoreOrientationRequest()) { - return SCREEN_ORIENTATION_UNSET; - } - if (!canSpecifyOrientation()) { + @ScreenOrientation + int getOrientation(@ScreenOrientation int candidate) { + final int orientation = super.getOrientation(candidate); + if (!canSpecifyOrientation(orientation)) { + mLastOrientationSource = null; // We only respect orientation of the focused TDA, which can be a child of this TDA. - return reduceOnAllTaskDisplayAreas((taskDisplayArea, orientation) -> { - if (taskDisplayArea == this || orientation != SCREEN_ORIENTATION_UNSET) { - return orientation; + return reduceOnAllTaskDisplayAreas((taskDisplayArea, taskOrientation) -> { + if (taskDisplayArea == this || taskOrientation != SCREEN_ORIENTATION_UNSET) { + return taskOrientation; } return taskDisplayArea.getOrientation(candidate); }, SCREEN_ORIENTATION_UNSET); } - final int orientation = super.getOrientation(candidate); if (orientation != SCREEN_ORIENTATION_UNSET && orientation != SCREEN_ORIENTATION_BEHIND) { ProtoLog.v(WM_DEBUG_ORIENTATION, @@ -1870,12 +1869,11 @@ final class TaskDisplayArea extends DisplayArea { } /** Whether this task display area can request orientation. */ - boolean canSpecifyOrientation() { - // Only allow to specify orientation if this TDA is not set to ignore orientation request, - // and it is the last focused one on this logical display that can request orientation - // request. - return !getIgnoreOrientationRequest() - && mDisplayContent.getOrientationRequestingTaskDisplayArea() == this; + boolean canSpecifyOrientation(@ScreenOrientation int orientation) { + // Only allow to specify orientation if this TDA is the last focused one on this logical + // display that can request orientation request. + return mDisplayContent.getOrientationRequestingTaskDisplayArea() == this + && !getIgnoreOrientationRequest(orientation); } void clearPreferredTopFocusableRootTask() { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 5c893de6b920f..5724f7199f8a7 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1419,9 +1419,9 @@ class WindowContainer extends ConfigurationContainer< * @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() { + boolean handlesOrientationChangeFromDescendant(int orientation) { final WindowContainer parent = getParent(); - return parent != null && parent.handlesOrientationChangeFromDescendant(); + return parent != null && parent.handlesOrientationChangeFromDescendant(orientation); } /** @@ -1513,7 +1513,8 @@ class WindowContainer extends ConfigurationContainer< // portrait but the task is still in landscape. While updating from display, // the task can be updated to portrait first so the configuration can be // computed in a consistent environment. - && (inMultiWindowMode() || !handlesOrientationChangeFromDescendant())) { + && (inMultiWindowMode() + || !handlesOrientationChangeFromDescendant(orientation))) { // Resolve the requested orientation. onConfigurationChanged(parent.getConfiguration()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 17ec19d3e6177..a017bd6a9436e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -517,7 +517,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Mimic the behavior that display doesn't handle app's requested orientation. final DisplayContent dc = activity.getTask().getDisplayContent(); doReturn(false).when(dc).onDescendantOrientationChanged(any()); - doReturn(false).when(dc).handlesOrientationChangeFromDescendant(); + doReturn(false).when(dc).handlesOrientationChangeFromDescendant(anyInt()); final int requestedOrientation; switch (newConfig.orientation) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index b87c5a364c824..10540dc5a9eee 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -18,6 +18,8 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; @@ -470,28 +472,6 @@ public class DisplayAreaTest extends WindowTestsBase { area.getOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR)); } - @Test - public void testSetIgnoreOrientationRequest() { - final DisplayArea.Tokens area = new DisplayArea.Tokens(mWm, ABOVE_TASKS, "test"); - final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY); - spyOn(token); - doReturn(mock(DisplayContent.class)).when(token).getDisplayContent(); - doNothing().when(token).setParent(any()); - final WindowState win = createWindowState(token); - spyOn(win); - doNothing().when(win).setParent(any()); - win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - token.addChild(win, 0); - area.addChild(token); - doReturn(true).when(win).isVisible(); - - assertEquals(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, area.getOrientation()); - - area.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - - assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSET, area.getOrientation()); - } - @Test public void testSetIgnoreOrientationRequest_notCallSuperOnDescendantOrientationChanged() { final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); @@ -514,7 +494,49 @@ public class DisplayAreaTest extends WindowTestsBase { } @Test - public void testSetIgnoreOrientationRequest_updateOrientationRequestingTaskDisplayArea() { + public void testSetIgnoreOrientationRequest_callSuperOnDescendantOrientationChangedNoSensor() { + final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); + final Task stack = + new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); + final ActivityRecord activity = stack.getTopNonFinishingActivity(); + + tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + activity.setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR); + + verify(tda).onDescendantOrientationChanged(any()); + verify(mDisplayContent).onDescendantOrientationChanged(any()); + + tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + activity.setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR); + + verify(tda).onDescendantOrientationChanged(any()); + verify(mDisplayContent).onDescendantOrientationChanged(any()); + } + + @Test + public void testSetIgnoreOrientationRequest_callSuperOnDescendantOrientationChangedLocked() { + final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); + final Task stack = + new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); + final ActivityRecord activity = stack.getTopNonFinishingActivity(); + + tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + activity.setRequestedOrientation(SCREEN_ORIENTATION_LOCKED); + + verify(tda).onDescendantOrientationChanged(any()); + verify(mDisplayContent).onDescendantOrientationChanged(any()); + + tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + activity.setRequestedOrientation(SCREEN_ORIENTATION_LOCKED); + + verify(tda).onDescendantOrientationChanged(any()); + verify(mDisplayContent).onDescendantOrientationChanged(any()); + } + + @Test + public void testGetOrientationRequestingTaskDisplayArea_updateOrientationTaskDisplayArea() { final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); final Task stack = new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); @@ -526,7 +548,7 @@ public class DisplayAreaTest extends WindowTestsBase { // TDA is no longer handling orientation request, clear the last focused TDA. tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isNull(); + assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda); // TDA now handles orientation request, update last focused TDA based on the focused app. tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java index 3ab4495bd7ca4..232b9b2897ccc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java @@ -17,6 +17,8 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; @@ -162,6 +164,30 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { verify(mDisplay, never()).onDescendantOrientationChanged(any()); } + @Test + public void testIgnoreOrientationRequest_displayReceiveOrientationChangeForNoSensor() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR); + + verify(mFirstRoot).onDescendantOrientationChanged(any()); + verify(mDisplay).onDescendantOrientationChanged(any()); + } + + @Test + public void testIgnoreOrientationRequest_displayReceiveOrientationChangeForLocked() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LOCKED); + + verify(mFirstRoot).onDescendantOrientationChanged(any()); + verify(mDisplay).onDescendantOrientationChanged(any()); + } + @Test public void testLaunchPortraitApp_fillsDisplayAreaGroup() { mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -214,6 +240,21 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { newTaskBounds.height() * newTaskBounds.height() / newTaskBounds.width()); } + @Test + public void testLaunchNoSensorApp_noSizeCompatAfterRotation() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR); + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); + assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); + + rotateDisplay(mDisplay, ROTATION_90); + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); + assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); + } + @Test public void testLaunchLandscapeApp_activityIsLetterboxForFixedOrientationInDisplayAreaGroup() { mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -235,6 +276,26 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { .isEqualTo(dagBounds.width() * dagBounds.width() / dagBounds.height()); } + @Test + public void testLaunchNoSensorApp_activityIsNotLetterboxForFixedOrientationDisplayAreaGroup() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR); + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); + } + + @Test + public void testLaunchLockedApp_activityIsNotLetterboxForFixedOrientationInDisplayAreaGroup() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LOCKED); + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); + } + @Test public void testLaunchLandscapeApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation() { mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -264,6 +325,20 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { assertThat(newActivityBounds.height()).isEqualTo(activityBounds.height()); } + @Test + public void testLaunchNoSensorApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation() { + mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + + prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR); + + rotateDisplay(mDisplay, ROTATION_90); + + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); + assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); + } + @Test public void testPlaceImeContainer_reparentToTargetDisplayAreaGroup() { setupImeWindow(); 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 e660db57fb694..582ddf21d7f93 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -29,8 +29,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT; import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -422,24 +423,26 @@ public class TaskDisplayAreaTests extends WindowTestsBase { // Activity on TDA1 is focused mDisplayContent.setFocusedApp(firstActivity); - assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue(); - assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); + final int testOrientation = SCREEN_ORIENTATION_PORTRAIT; + + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); // No focused app, TDA1 is still recorded as last focused. mDisplayContent.setFocusedApp(null); - assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue(); - assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); // Activity on TDA2 is focused mDisplayContent.setFocusedApp(secondActivity); - assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); - assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); } @Test - public void testIsLastFocused_onlyCountIfTaskDisplayAreaHandlesOrientationRequest() { + public void testCanSpecifyOrientation() { final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", @@ -455,34 +458,88 @@ public class TaskDisplayAreaTests extends WindowTestsBase { firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); - // Activity on TDA1 is focused, but TDA1 doesn't respect orientation request + final int testOrientation = SCREEN_ORIENTATION_PORTRAIT; + + // Activity on TDA1 is focused, but TDA1 cannot specify orientation because + // ignoreOrientationRequest is true + // Activity on TDA2 has ignoreOrientationRequest false but it doesn't have focus so it + // cannot specify orientation mDisplayContent.setFocusedApp(firstActivity); - assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); - assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); - // Activity on TDA2 is focused, and TDA2 respects orientation request + // Activity on TDA1 is not focused, and so it cannot specify orientation + // Activity on TDA2 is focused, and it can specify orientation because + // ignoreOrientationRequest is false mDisplayContent.setFocusedApp(secondActivity); - assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); - assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); } @Test - public void testIgnoreOrientationRequest() { - final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); - final Task task = taskDisplayArea.createRootTask( + public void testCanSpecifyOrientationNoSensor() { + final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( + mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", + FEATURE_VENDOR_FIRST); + final Task firstRootTask = firstTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); + final Task secondRootTask = secondTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm) + .setTask(firstRootTask).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm) + .setTask(secondRootTask).build(); + firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); - mDisplayContent.setFocusedApp(activity); - activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + final int testOrientation = SCREEN_ORIENTATION_NOSENSOR; - assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); + // ignoreOrientationRequest is always false for SCREEN_ORIENTATION_NOSENSOR so + // only the TDAs with focus can specify orientations + mDisplayContent.setFocusedApp(firstActivity); - taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); - assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET); + mDisplayContent.setFocusedApp(secondActivity); + + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); + } + + @Test + public void testCanSpecifyOrientationLocked() { + final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( + mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", + FEATURE_VENDOR_FIRST); + final Task firstRootTask = firstTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final Task secondRootTask = secondTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm) + .setTask(firstRootTask).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm) + .setTask(secondRootTask).build(); + firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + + final int testOrientation = SCREEN_ORIENTATION_LOCKED; + + // ignoreOrientationRequest is always false for SCREEN_ORIENTATION_NOSENSOR so + // only the TDAs with focus can specify orientations + mDisplayContent.setFocusedApp(firstActivity); + + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); + + mDisplayContent.setFocusedApp(secondActivity); + + assertThat(firstTaskDisplayArea.canSpecifyOrientation(testOrientation)).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation(testOrientation)).isTrue(); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 0c8e89a8d0103..eac67770cf36c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -57,6 +57,7 @@ import static org.junit.Assert.assertNull; 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.eq; import static org.mockito.ArgumentMatchers.same; @@ -392,12 +393,16 @@ public class TaskTests extends WindowTestsBase { leafTask1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_HOME); leafTask2.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_STANDARD); + // We need to use an orientation that is not an exception for the + // ignoreOrientationRequest flag. + final int orientation = SCREEN_ORIENTATION_PORTRAIT; + assertEquals(leafTask2, rootTask.getTopChild()); - assertTrue(rootTask.handlesOrientationChangeFromDescendant()); + assertTrue(rootTask.handlesOrientationChangeFromDescendant(orientation)); // Treat orientation request from home as handled. - assertTrue(leafTask1.handlesOrientationChangeFromDescendant()); + assertTrue(leafTask1.handlesOrientationChangeFromDescendant(orientation)); // Orientation request from standard activity in multi window will not be handled. - assertFalse(leafTask2.handlesOrientationChangeFromDescendant()); + assertFalse(leafTask2.handlesOrientationChangeFromDescendant(orientation)); } @Test @@ -636,7 +641,8 @@ public class TaskTests extends WindowTestsBase { doReturn(parentWindowContainer).when(task).getParent(); doReturn(display.getDefaultTaskDisplayArea()).when(task).getDisplayArea(); doReturn(rootTask).when(task).getRootTask(); - doReturn(true).when(parentWindowContainer).handlesOrientationChangeFromDescendant(); + doReturn(true).when(parentWindowContainer) + .handlesOrientationChangeFromDescendant(anyInt()); // Setting app to fixed portrait fits within parent, but Task shouldn't adjust the // bounds because its parent says it will handle it at a later time. 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 ef3ddb7b13027..ed7d123846622 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -662,13 +662,13 @@ public class WindowContainerTests extends WindowTestsBase { public void testSetOrientation() { final TestWindowContainer root = spy(new TestWindowContainerBuilder(mWm).build()); final TestWindowContainer child = spy(root.addChildWindow()); - doReturn(true).when(root).handlesOrientationChangeFromDescendant(); + doReturn(true).when(root).handlesOrientationChangeFromDescendant(anyInt()); child.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN); child.setOrientation(SCREEN_ORIENTATION_PORTRAIT); // The ancestor should decide whether to dispatch the configuration change. verify(child, never()).onConfigurationChanged(any()); - doReturn(false).when(root).handlesOrientationChangeFromDescendant(); + doReturn(false).when(root).handlesOrientationChangeFromDescendant(anyInt()); child.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); // The ancestor doesn't handle the request so the descendant applies the change directly. verify(child).onConfigurationChanged(any()); @@ -843,11 +843,14 @@ public class WindowContainerTests extends WindowTestsBase { final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); final TestWindowContainer root = spy(builder.build()); - final TestWindowContainer child = root.addChildWindow(); - assertFalse(child.handlesOrientationChangeFromDescendant()); + // We use an orientation that is not an exception for the ignoreOrientationRequest flag + final int orientation = SCREEN_ORIENTATION_PORTRAIT; - Mockito.doReturn(true).when(root).handlesOrientationChangeFromDescendant(); - assertTrue(child.handlesOrientationChangeFromDescendant()); + final TestWindowContainer child = root.addChildWindow(); + assertFalse(child.handlesOrientationChangeFromDescendant(orientation)); + + Mockito.doReturn(true).when(root).handlesOrientationChangeFromDescendant(anyInt()); + assertTrue(child.handlesOrientationChangeFromDescendant(orientation)); } @Test