diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index ba901549f2b56..eba4fd21166d2 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -238,10 +238,10 @@ public final class WindowContainerTransaction implements Parcelable { } /** - * Sets whether a container should ignore the orientation request from apps below it. It - * currently only applies to {@link com.android.server.wm.TaskDisplayArea}. When {@code false}, - * it may rotate based on the orientation request; When {@code true}, it can never specify - * orientation, but shows the fixed-orientation apps in the letterbox. + * Sets whether a container should ignore the orientation request from apps and windows below + * it. It currently only applies to {@link com.android.server.wm.DisplayArea}. When + * {@code false}, it may rotate based on the orientation request; When {@code true}, it can + * never specify orientation, but shows the fixed-orientation apps below it in the letterbox. * @hide */ @NonNull diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 59727d5b25559..31dae22df4546 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -301,12 +301,6 @@ "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", @@ -529,6 +523,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/Task.java" }, + "-1480772131": { + "message": "No app or window is requesting an orientation, return %d for display id=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayContent.java" + }, "-1474292612": { "message": "Could not find task for id: %d", "level": "DEBUG", @@ -2515,12 +2515,6 @@ "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" - }, "849147756": { "message": "Finish collecting in transition %d", "level": "VERBOSE", @@ -2923,6 +2917,12 @@ "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", @@ -3133,6 +3133,18 @@ "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" + }, + "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" + }, "1653210583": { "message": "Removing app %s delayed=%b animation=%s animating=%b", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 1718b2aef0c99..e4f8d8ba6399c 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -41,6 +41,7 @@ import android.window.IDisplayAreaOrganizer; import com.android.internal.protolog.common.ProtoLog; import com.android.server.policy.WindowManagerPolicy; +import java.io.PrintWriter; import java.util.Comparator; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -71,6 +72,13 @@ public class DisplayArea extends WindowContainer { IDisplayAreaOrganizer mOrganizer; private final Configuration mTmpConfiguration = new Configuration(); + /** + * Whether this {@link DisplayArea} should ignore fixed-orientation request. If {@code true}, it + * can never specify orientation, but shows the fixed-orientation apps below it in the + * letterbox; otherwise, it rotates based on the fixed-orientation request. + */ + protected boolean mIgnoreOrientationRequest; + DisplayArea(WindowManagerService wms, Type type, String name) { this(wms, type, name, FEATURE_UNDEFINED); } @@ -127,6 +135,52 @@ public class DisplayArea extends WindowContainer { } } + @Override + int getOrientation(int candidate) { + mLastOrientationSource = null; + if (mIgnoreOrientationRequest) { + return SCREEN_ORIENTATION_UNSET; + } + + return super.getOrientation(candidate); + } + + /** + * Sets whether this {@link DisplayArea} should ignore fixed-orientation request from apps and + * windows below it. + * + * @return Whether the display orientation changed after calling this method. + */ + boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) { + if (mIgnoreOrientationRequest == ignoreOrientationRequest) { + return false; + } + mIgnoreOrientationRequest = ignoreOrientationRequest; + + // Check whether we should notify Display to update orientation. + if (mDisplayContent == null) { + return false; + } + + // The orientation request from this DA may now be respected. + if (!ignoreOrientationRequest) { + return mDisplayContent.updateOrientation(); + } + + final int lastOrientation = mDisplayContent.getLastOrientation(); + final WindowContainer lastOrientationSource = mDisplayContent.getLastOrientationSource(); + if (lastOrientation == SCREEN_ORIENTATION_UNSET + || lastOrientation == SCREEN_ORIENTATION_UNSPECIFIED) { + // Orientation won't be changed. + return false; + } + if (lastOrientationSource == null || lastOrientationSource.isDescendantOf(this)) { + // Try update if the orientation may be affected. + return mDisplayContent.updateOrientation(); + } + return false; + } + /** * When a {@link DisplayArea} is repositioned, it should only be moved among its siblings of the * same {@link Type}. @@ -199,6 +253,14 @@ public class DisplayArea extends WindowContainer { proto.end(token); } + @Override + void dump(PrintWriter pw, String prefix, boolean dumpAll) { + super.dump(pw, prefix, dumpAll); + if (mIgnoreOrientationRequest) { + pw.println(prefix + "mIgnoreOrientationRequest=true"); + } + } + @Override long getProtoFieldId() { return DISPLAY_AREA; @@ -409,6 +471,10 @@ public class DisplayArea extends WindowContainer { @Override int getOrientation(int candidate) { mLastOrientationSource = null; + if (mIgnoreOrientationRequest) { + 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 e7f0e3eb7a76c..0bade7db911f1 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -28,8 +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.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; @@ -2347,6 +2347,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @Override int getOrientation() { mLastOrientationSource = null; + if (mIgnoreOrientationRequest) { + // 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()) { @@ -2363,19 +2370,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } final int orientation = super.getOrientation(); - if (orientation != SCREEN_ORIENTATION_UNSET && orientation != SCREEN_ORIENTATION_BEHIND) { + if (orientation == SCREEN_ORIENTATION_UNSET) { + // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation ProtoLog.v(WM_DEBUG_ORIENTATION, - "App is requesting an orientation, return %d for display id=%d", - orientation, mDisplayId); - return orientation; + "No app or window is requesting an orientation, return %d for display id=%d", + SCREEN_ORIENTATION_UNSPECIFIED, mDisplayId); + return SCREEN_ORIENTATION_UNSPECIFIED; } - 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(); + return orientation; } void updateDisplayInfo() { @@ -4243,6 +4246,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @Override int getOrientation(int candidate) { + if (mIgnoreOrientationRequest) { + return SCREEN_ORIENTATION_UNSET; + } + // IME does not participate in orientation. return candidate; } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index d69fb0b823d07..830ad5d6043bc 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -29,10 +29,12 @@ 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.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK; @@ -149,13 +151,6 @@ final class TaskDisplayArea extends DisplayArea { */ private boolean mRemoved; - /** - * Whether the task display area should ignore fixed-orientation request. If {@code true}, it - * can never specify orientation, but show the fixed-orientation apps in the letterbox; - * otherwise, it rotates based on the fixed-orientation request when it has the focus. - */ - private boolean mIgnoreOrientationRequest; - /** * The id of a leaf task that most recently being moved to front. */ @@ -654,28 +649,9 @@ final class TaskDisplayArea extends DisplayArea { } } - /** - * Sets whether the task display area should ignore fixed-orientation request from apps. - * - * @return Whether the display orientation changed - */ - boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) { - if (mIgnoreOrientationRequest == ignoreOrientationRequest) { - return false; - } - - mIgnoreOrientationRequest = ignoreOrientationRequest; - if (isLastFocused()) { - // Update orientation if this TDA is the last focused, otherwise it shouldn't affect - // the display. - return mDisplayContent.updateOrientation(); - } - - return false; - } - @Override int getOrientation(int candidate) { + mLastOrientationSource = null; // Only allow to specify orientation if this TDA is not set to ignore orientation request, // and it has the focus. if (mIgnoreOrientationRequest || !isLastFocused()) { @@ -708,7 +684,21 @@ final class TaskDisplayArea extends DisplayArea { return SCREEN_ORIENTATION_UNSPECIFIED; } - return super.getOrientation(candidate); + 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(); } @Override diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 6a5784435ad66..5e07f5187c546 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -380,24 +380,18 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return effects; } - private int applyTaskDisplayAreaChanges(TaskDisplayArea taskDisplayArea, - WindowContainerTransaction.Change c) { - int effects = applyDisplayAreaChanges(taskDisplayArea, c); - if ((c.getChangeMask() - & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) { - if (taskDisplayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) { - effects |= TRANSACT_EFFECTS_LIFECYCLE; - } - } - - return effects; - } - - private int applyDisplayAreaChanges(WindowContainer container, + private int applyDisplayAreaChanges(DisplayArea displayArea, WindowContainerTransaction.Change c) { final int[] effects = new int[1]; - container.forAllTasks(task -> { + if ((c.getChangeMask() + & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) { + if (displayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) { + effects[0] |= TRANSACT_EFFECTS_LIFECYCLE; + } + } + + displayArea.forAllTasks(task -> { Task tr = (Task) task; if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) { if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) { @@ -475,10 +469,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub int effects = applyChanges(wc, c); - if (wc instanceof TaskDisplayArea) { - effects |= applyTaskDisplayAreaChanges((TaskDisplayArea) wc, c); - } else if (wc instanceof DisplayArea) { - effects |= applyDisplayAreaChanges(wc, c); + if (wc instanceof DisplayArea) { + effects |= applyDisplayAreaChanges(wc.asDisplayArea(), c); } else if (wc instanceof Task) { effects |= applyTaskChanges(wc.asTask(), c); } 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 f88530c1f23e7..01c1f1f73ee9a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -438,6 +438,28 @@ public class DisplayAreaTest { area.getOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR)); } + @Test + public void testSetIgnoreOrientationRequest() { + final DisplayArea.Tokens area = new DisplayArea.Tokens(mWms, 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()); + } + private static class TestDisplayArea extends DisplayArea { private TestDisplayArea(WindowManagerService wms, Rect bounds) { super(wms, ANY, "half display area"); 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 d2b7ac4c3b248..7975899dacbcb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -1002,14 +1002,18 @@ public class TaskRecordTests extends WindowTestsBase { public void testNotSpecifyOrientationByFloatingTask() { final Task task = getTestTask(); final ActivityRecord activity = task.getTopMostActivity(); + final WindowContainer parentContainer = task.getParent(); final TaskDisplayArea taskDisplayArea = task.getDisplayArea(); activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parentContainer.getOrientation()); assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation()); task.setWindowingMode(WINDOWING_MODE_PINNED); - assertEquals(SCREEN_ORIENTATION_UNSET, taskDisplayArea.getOrientation()); + // TDA returns the last orientation when child returns UNSET + assertEquals(SCREEN_ORIENTATION_UNSET, parentContainer.getOrientation()); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 0152fc607f735..45534ddd57b55 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -366,7 +366,7 @@ public class WindowOrganizerTests extends WindowTestsBase { } @Test - public void testSetIgnoreOrientationRequest() { + public void testSetIgnoreOrientationRequest_taskDisplayArea() { removeGlobalMinSizeRestriction(); final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); final Task stack = taskDisplayArea.createStack( @@ -378,7 +378,7 @@ public class WindowOrganizerTests extends WindowTestsBase { activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); // TDA returns UNSET when ignoreOrientationRequest == true - // DC is UNSPECIFIED because it is using the previous (default) when TDA returns UNSET. + // DC is UNSPECIFIED when child returns UNSET assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET); assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); @@ -399,8 +399,40 @@ public class WindowOrganizerTests extends WindowTestsBase { mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); // TDA returns UNSET when ignoreOrientationRequest == true - // DC is LANDSCAPE because it is using the previous when TDA returns UNSET. + // DC is UNSPECIFIED when child returns UNSET assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET); + assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); + } + + @Test + public void testSetIgnoreOrientationRequest_displayContent() { + removeGlobalMinSizeRestriction(); + final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + final Task stack = taskDisplayArea.createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true) + .setStack(stack).build(); + mDisplayContent.setFocusedApp(activity); + activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + + // DC uses the orientation request from app + assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); + + WindowContainerTransaction t = new WindowContainerTransaction(); + t.setIgnoreOrientationRequest( + mDisplayContent.mRemoteToken.toWindowContainerToken(), + true /* ignoreOrientationRequest */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); + + // DC returns UNSPECIFIED when ignoreOrientationRequest == true + assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); + + t.setIgnoreOrientationRequest( + mDisplayContent.mRemoteToken.toWindowContainerToken(), + false /* ignoreOrientationRequest */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); + + // DC uses the orientation request from app after mIgnoreOrientationRequest is set to false assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); }