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
This commit is contained in:
Massimo Carli
2022-11-23 13:48:01 +00:00
parent f9bfa03273
commit 4b038dd78f
13 changed files with 308 additions and 138 deletions

View File

@@ -3907,12 +3907,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",
@@ -4141,6 +4135,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",

View File

@@ -7735,10 +7735,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.
@@ -8311,11 +8307,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
@Override
boolean handlesOrientationChangeFromDescendant() {
boolean handlesOrientationChangeFromDescendant(int orientation) {
if (shouldIgnoreOrientationRequests()) {
return false;
}
return super.handlesOrientationChangeFromDescendant();
return super.handlesOrientationChangeFromDescendant(orientation);
}
/**
@@ -8337,7 +8333,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.

View File

@@ -35,6 +35,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;
@@ -142,26 +144,30 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
}
@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);
}
@@ -225,6 +231,23 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
}
}
/**
* @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
@@ -691,11 +714,9 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
}
@Override
@ScreenOrientation
int getOrientation(int candidate) {
mLastOrientationSource = null;
if (getIgnoreOrientationRequest()) {
return SCREEN_ORIENTATION_UNSET;
}
// Find a window requesting orientation.
final WindowState win = getWindow(mGetOrientingWindow);

View File

@@ -1601,13 +1601,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;
}
@@ -1630,8 +1632,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
@Override
boolean handlesOrientationChangeFromDescendant() {
return !getIgnoreOrientationRequest()
boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) {
return !getIgnoreOrientationRequest(orientation)
&& !getDisplayRotation().isFixedToUserRotation();
}
@@ -1732,7 +1734,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) {
@@ -2740,15 +2742,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
@@ -2764,6 +2757,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,
@@ -3890,18 +3893,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;
}
/**
@@ -5104,13 +5096,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

View File

@@ -144,6 +144,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;
@@ -2674,8 +2675,8 @@ class Task extends TaskFragment {
}
@Override
boolean handlesOrientationChangeFromDescendant() {
if (!super.handlesOrientationChangeFromDescendant()) {
boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) {
if (!super.handlesOrientationChangeFromDescendant(orientation)) {
return false;
}
@@ -2690,7 +2691,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) {

View File

@@ -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<WindowContainer> {
}
@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,
@@ -1872,12 +1871,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
/** 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() {

View File

@@ -1459,9 +1459,9 @@ class WindowContainer<E extends 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);
}
/**
@@ -1553,7 +1553,8 @@ class WindowContainer<E extends 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());
}

View File

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

View File

@@ -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 */);

View File

@@ -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;
@@ -169,6 +171,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 */);
@@ -221,6 +247,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 */);
@@ -242,6 +283,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 */);
@@ -271,6 +332,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();

View File

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

View File

@@ -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;
@@ -393,12 +394,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
@@ -655,7 +660,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.

View File

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