Support ignoring orientation request on DisplayArea level

1. Move the mIgnoreOrientationRequest to DisplayArea so that one can set
it on DisplayContent and DisplayAreaGroup to ignore orientation request
from non-app windows.
2. Have the DisplayContent to return SCREEN_ORIENTATION_UNSPECIFIED when
it is SCREEN_ORIENTATION_UNSET so that the display respects sensor
rotation.
3. Move the logic of using last orientation back to TDA, otherwise when
TDA returns UNSET because ignore is set, DC may incorrectly uses the
last orientation.

Bug: 170725334
Bug: 155431879
Test: manual: test with setting TDA and DC to ignore
Test: atest WmTests:TaskDisplayAreaTests
Test: atest WmTests:WindowOrganizerTests
Test: atest WmTests:DisplayAreaTest
Test: atest WmTests:DisplayContentTests
Change-Id: Ie92b059c453204799a58efd39dc9b23f7d62816d
This commit is contained in:
Chris Li
2020-10-13 15:13:46 -07:00
parent 4a11c4408f
commit d93fff6c0f
9 changed files with 203 additions and 78 deletions

View File

@@ -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

View File

@@ -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",

View File

@@ -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<T extends WindowContainer> extends WindowContainer<T> {
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<T extends WindowContainer> extends WindowContainer<T> {
}
}
@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<T extends WindowContainer> extends WindowContainer<T> {
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<T extends WindowContainer> extends WindowContainer<T> {
@Override
int getOrientation(int candidate) {
mLastOrientationSource = null;
if (mIgnoreOrientationRequest) {
return SCREEN_ORIENTATION_UNSET;
}
// Find a window requesting orientation.
final WindowState win = getWindow(mGetOrientingWindow);

View File

@@ -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;
}

View File

@@ -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<Task> {
*/
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<Task> {
}
}
/**
* 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<Task> {
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

View File

@@ -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);
}

View File

@@ -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<T extends WindowContainer> extends DisplayArea<T> {
private TestDisplayArea(WindowManagerService wms, Rect bounds) {
super(wms, ANY, "half display area");

View File

@@ -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

View File

@@ -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);
}