Remember and revert camera compat orientation change when camera closes
Also, revert rotation after the top activity stops being SCREEN_ORIENTATION_NOSENSOR and add a RotationReversionController to manage the reversion behaviour for camera compat, NOSENSOR and half-fold transitions. Bug: 264186382 Bug: 267751289 Test: tested locally Test: atest WmTests:DisplayRotationCompatPolicyTests Test: atest WmTests:DisplayContentTests Change-Id: I0033a5de7c4a7b886b241088577c32715ecccc57
This commit is contained in:
@@ -469,6 +469,18 @@
|
|||||||
"group": "WM_DEBUG_TASKS",
|
"group": "WM_DEBUG_TASKS",
|
||||||
"at": "com\/android\/server\/wm\/RecentTasks.java"
|
"at": "com\/android\/server\/wm\/RecentTasks.java"
|
||||||
},
|
},
|
||||||
|
"-1643780158": {
|
||||||
|
"message": "Saving original orientation before camera compat, last orientation is %d",
|
||||||
|
"level": "VERBOSE",
|
||||||
|
"group": "WM_DEBUG_ORIENTATION",
|
||||||
|
"at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java"
|
||||||
|
},
|
||||||
|
"-1639406696": {
|
||||||
|
"message": "NOSENSOR override detected",
|
||||||
|
"level": "VERBOSE",
|
||||||
|
"group": "WM_DEBUG_ORIENTATION",
|
||||||
|
"at": "com\/android\/server\/wm\/DisplayRotationReversionController.java"
|
||||||
|
},
|
||||||
"-1638958146": {
|
"-1638958146": {
|
||||||
"message": "Removing activity %s from task=%s adding to task=%s Callers=%s",
|
"message": "Removing activity %s from task=%s adding to task=%s Callers=%s",
|
||||||
"level": "INFO",
|
"level": "INFO",
|
||||||
@@ -751,6 +763,12 @@
|
|||||||
"group": "WM_DEBUG_IME",
|
"group": "WM_DEBUG_IME",
|
||||||
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
|
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
|
||||||
},
|
},
|
||||||
|
"-1397175017": {
|
||||||
|
"message": "Other orientation overrides are in place: not reverting",
|
||||||
|
"level": "VERBOSE",
|
||||||
|
"group": "WM_DEBUG_ORIENTATION",
|
||||||
|
"at": "com\/android\/server\/wm\/DisplayRotationReversionController.java"
|
||||||
|
},
|
||||||
"-1394745488": {
|
"-1394745488": {
|
||||||
"message": "ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s",
|
"message": "ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s",
|
||||||
"level": "INFO",
|
"level": "INFO",
|
||||||
@@ -1669,6 +1687,12 @@
|
|||||||
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
|
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
|
||||||
"at": "com\/android\/server\/wm\/Transition.java"
|
"at": "com\/android\/server\/wm\/Transition.java"
|
||||||
},
|
},
|
||||||
|
"-529187878": {
|
||||||
|
"message": "Reverting orientation after camera compat force rotation",
|
||||||
|
"level": "VERBOSE",
|
||||||
|
"group": "WM_DEBUG_ORIENTATION",
|
||||||
|
"at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java"
|
||||||
|
},
|
||||||
"-521613870": {
|
"-521613870": {
|
||||||
"message": "App died during pause, not stopping: %s",
|
"message": "App died during pause, not stopping: %s",
|
||||||
"level": "VERBOSE",
|
"level": "VERBOSE",
|
||||||
@@ -2365,6 +2389,12 @@
|
|||||||
"group": "WM_DEBUG_FOCUS_LIGHT",
|
"group": "WM_DEBUG_FOCUS_LIGHT",
|
||||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||||
},
|
},
|
||||||
|
"138097009": {
|
||||||
|
"message": "NOSENSOR override is absent: reverting",
|
||||||
|
"level": "VERBOSE",
|
||||||
|
"group": "WM_DEBUG_ORIENTATION",
|
||||||
|
"at": "com\/android\/server\/wm\/DisplayRotationReversionController.java"
|
||||||
|
},
|
||||||
"140319294": {
|
"140319294": {
|
||||||
"message": "IME target changed within ActivityRecord",
|
"message": "IME target changed within ActivityRecord",
|
||||||
"level": "DEBUG",
|
"level": "DEBUG",
|
||||||
|
|||||||
@@ -746,6 +746,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
*/
|
*/
|
||||||
DisplayWindowPolicyControllerHelper mDwpcHelper;
|
DisplayWindowPolicyControllerHelper mDwpcHelper;
|
||||||
|
|
||||||
|
private final DisplayRotationReversionController mRotationReversionController;
|
||||||
|
|
||||||
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
|
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
|
||||||
WindowStateAnimator winAnimator = w.mWinAnimator;
|
WindowStateAnimator winAnimator = w.mWinAnimator;
|
||||||
final ActivityRecord activity = w.mActivityRecord;
|
final ActivityRecord activity = w.mActivityRecord;
|
||||||
@@ -1170,6 +1172,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabled(
|
mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabled(
|
||||||
/* checkDeviceConfig */ false)
|
/* checkDeviceConfig */ false)
|
||||||
? new DisplayRotationCompatPolicy(this) : null;
|
? new DisplayRotationCompatPolicy(this) : null;
|
||||||
|
mRotationReversionController = new DisplayRotationReversionController(this);
|
||||||
|
|
||||||
mInputMonitor = new InputMonitor(mWmService, this);
|
mInputMonitor = new InputMonitor(mWmService, this);
|
||||||
mInsetsPolicy = new InsetsPolicy(mInsetsStateController, this);
|
mInsetsPolicy = new InsetsPolicy(mInsetsStateController, this);
|
||||||
@@ -1283,6 +1286,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
.show(mOverlayLayer);
|
.show(mOverlayLayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DisplayRotationReversionController getRotationReversionController() {
|
||||||
|
return mRotationReversionController;
|
||||||
|
}
|
||||||
|
|
||||||
boolean isReady() {
|
boolean isReady() {
|
||||||
// The display is ready when the system and the individual display are both ready.
|
// The display is ready when the system and the individual display are both ready.
|
||||||
return mWmService.mDisplayReady && mDisplayReady;
|
return mWmService.mDisplayReady && mDisplayReady;
|
||||||
@@ -1665,9 +1672,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean updateOrientation(boolean forceUpdate) {
|
private boolean updateOrientation(boolean forceUpdate) {
|
||||||
|
final WindowContainer prevOrientationSource = mLastOrientationSource;
|
||||||
final int orientation = getOrientation();
|
final int orientation = getOrientation();
|
||||||
// The last orientation source is valid only after getOrientation.
|
// The last orientation source is valid only after getOrientation.
|
||||||
final WindowContainer orientationSource = getLastOrientationSource();
|
final WindowContainer orientationSource = getLastOrientationSource();
|
||||||
|
if (orientationSource != prevOrientationSource
|
||||||
|
&& mRotationReversionController.isRotationReversionEnabled()) {
|
||||||
|
mRotationReversionController.updateForNoSensorOverride();
|
||||||
|
}
|
||||||
final ActivityRecord r =
|
final ActivityRecord r =
|
||||||
orientationSource != null ? orientationSource.asActivityRecord() : null;
|
orientationSource != null ? orientationSource.asActivityRecord() : null;
|
||||||
if (r != null) {
|
if (r != null) {
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ import static com.android.server.wm.DisplayRotationProto.IS_FIXED_TO_USER_ROTATI
|
|||||||
import static com.android.server.wm.DisplayRotationProto.LAST_ORIENTATION;
|
import static com.android.server.wm.DisplayRotationProto.LAST_ORIENTATION;
|
||||||
import static com.android.server.wm.DisplayRotationProto.ROTATION;
|
import static com.android.server.wm.DisplayRotationProto.ROTATION;
|
||||||
import static com.android.server.wm.DisplayRotationProto.USER_ROTATION;
|
import static com.android.server.wm.DisplayRotationProto.USER_ROTATION;
|
||||||
|
import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_CAMERA_COMPAT;
|
||||||
|
import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_HALF_FOLD;
|
||||||
|
import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_NOSENSOR;
|
||||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
||||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||||
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE;
|
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE;
|
||||||
@@ -106,6 +109,9 @@ public class DisplayRotation {
|
|||||||
int mExit;
|
int mExit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
final FoldController mFoldController;
|
||||||
|
|
||||||
private final WindowManagerService mService;
|
private final WindowManagerService mService;
|
||||||
private final DisplayContent mDisplayContent;
|
private final DisplayContent mDisplayContent;
|
||||||
private final DisplayPolicy mDisplayPolicy;
|
private final DisplayPolicy mDisplayPolicy;
|
||||||
@@ -127,8 +133,6 @@ public class DisplayRotation {
|
|||||||
private OrientationListener mOrientationListener;
|
private OrientationListener mOrientationListener;
|
||||||
private StatusBarManagerInternal mStatusBarManagerInternal;
|
private StatusBarManagerInternal mStatusBarManagerInternal;
|
||||||
private SettingsObserver mSettingsObserver;
|
private SettingsObserver mSettingsObserver;
|
||||||
@Nullable
|
|
||||||
private FoldController mFoldController;
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final DeviceStateController mDeviceStateController;
|
private final DeviceStateController mDeviceStateController;
|
||||||
|
|
||||||
@@ -279,7 +283,11 @@ public class DisplayRotation {
|
|||||||
if (mSupportAutoRotation && mContext.getResources().getBoolean(
|
if (mSupportAutoRotation && mContext.getResources().getBoolean(
|
||||||
R.bool.config_windowManagerHalfFoldAutoRotateOverride)) {
|
R.bool.config_windowManagerHalfFoldAutoRotateOverride)) {
|
||||||
mFoldController = new FoldController();
|
mFoldController = new FoldController();
|
||||||
|
} else {
|
||||||
|
mFoldController = null;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
mFoldController = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,6 +345,11 @@ public class DisplayRotation {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean useDefaultSettingsProvider() {
|
||||||
|
return isDefaultDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the configuration which may have different values depending on current user, e.g.
|
* Updates the configuration which may have different values depending on current user, e.g.
|
||||||
* runtime resource overlay.
|
* runtime resource overlay.
|
||||||
@@ -870,7 +883,7 @@ public class DisplayRotation {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setUserRotation(int userRotationMode, int userRotation) {
|
void setUserRotation(int userRotationMode, int userRotation) {
|
||||||
mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED;
|
mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED;
|
||||||
if (isDefaultDisplay) {
|
if (useDefaultSettingsProvider()) {
|
||||||
// We'll be notified via settings listener, so we don't need to update internal values.
|
// We'll be notified via settings listener, so we don't need to update internal values.
|
||||||
final ContentResolver res = mContext.getContentResolver();
|
final ContentResolver res = mContext.getContentResolver();
|
||||||
final int accelerometerRotation =
|
final int accelerometerRotation =
|
||||||
@@ -1814,7 +1827,7 @@ public class DisplayRotation {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) {
|
if (mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) {
|
||||||
return !(isTabletop ^ mTabletopRotations.contains(mRotation));
|
return isTabletop == mTabletopRotations.contains(mRotation);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1838,14 +1851,17 @@ public class DisplayRotation {
|
|||||||
return mDeviceState == DeviceStateController.DeviceState.OPEN
|
return mDeviceState == DeviceStateController.DeviceState.OPEN
|
||||||
&& !mShouldIgnoreSensorRotation // Ignore if the hinge angle still moving
|
&& !mShouldIgnoreSensorRotation // Ignore if the hinge angle still moving
|
||||||
&& mInHalfFoldTransition
|
&& mInHalfFoldTransition
|
||||||
&& mHalfFoldSavedRotation != -1 // Ignore if we've already reverted.
|
&& mDisplayContent.getRotationReversionController().isOverrideActive(
|
||||||
|
REVERSION_TYPE_HALF_FOLD)
|
||||||
&& mUserRotationMode
|
&& mUserRotationMode
|
||||||
== WindowManagerPolicy.USER_ROTATION_LOCKED; // Ignore if we're unlocked.
|
== WindowManagerPolicy.USER_ROTATION_LOCKED; // Ignore if we're unlocked.
|
||||||
}
|
}
|
||||||
|
|
||||||
int revertOverriddenRotation() {
|
int revertOverriddenRotation() {
|
||||||
int savedRotation = mHalfFoldSavedRotation;
|
int savedRotation = mHalfFoldSavedRotation;
|
||||||
mHalfFoldSavedRotation = -1;
|
mHalfFoldSavedRotation = -1;
|
||||||
|
mDisplayContent.getRotationReversionController()
|
||||||
|
.revertOverride(REVERSION_TYPE_HALF_FOLD);
|
||||||
mInHalfFoldTransition = false;
|
mInHalfFoldTransition = false;
|
||||||
return savedRotation;
|
return savedRotation;
|
||||||
}
|
}
|
||||||
@@ -1865,6 +1881,8 @@ public class DisplayRotation {
|
|||||||
&& mDeviceState != DeviceStateController.DeviceState.HALF_FOLDED) {
|
&& mDeviceState != DeviceStateController.DeviceState.HALF_FOLDED) {
|
||||||
// The device has transitioned to HALF_FOLDED state: save the current rotation and
|
// The device has transitioned to HALF_FOLDED state: save the current rotation and
|
||||||
// update the device rotation.
|
// update the device rotation.
|
||||||
|
mDisplayContent.getRotationReversionController().beforeOverrideApplied(
|
||||||
|
REVERSION_TYPE_HALF_FOLD);
|
||||||
mHalfFoldSavedRotation = mRotation;
|
mHalfFoldSavedRotation = mRotation;
|
||||||
mDeviceState = newState;
|
mDeviceState = newState;
|
||||||
// Now mFoldState is set to HALF_FOLDED, the overrideFrozenRotation function will
|
// Now mFoldState is set to HALF_FOLDED, the overrideFrozenRotation function will
|
||||||
@@ -2069,6 +2087,8 @@ public class DisplayRotation {
|
|||||||
final int mHalfFoldSavedRotation;
|
final int mHalfFoldSavedRotation;
|
||||||
final boolean mInHalfFoldTransition;
|
final boolean mInHalfFoldTransition;
|
||||||
final DeviceStateController.DeviceState mDeviceState;
|
final DeviceStateController.DeviceState mDeviceState;
|
||||||
|
@Nullable final boolean[] mRotationReversionSlots;
|
||||||
|
|
||||||
@Nullable final String mDisplayRotationCompatPolicySummary;
|
@Nullable final String mDisplayRotationCompatPolicySummary;
|
||||||
|
|
||||||
Record(DisplayRotation dr, int fromRotation, int toRotation) {
|
Record(DisplayRotation dr, int fromRotation, int toRotation) {
|
||||||
@@ -2109,6 +2129,8 @@ public class DisplayRotation {
|
|||||||
? null
|
? null
|
||||||
: dc.mDisplayRotationCompatPolicy
|
: dc.mDisplayRotationCompatPolicy
|
||||||
.getSummaryForDisplayRotationHistoryRecord();
|
.getSummaryForDisplayRotationHistoryRecord();
|
||||||
|
mRotationReversionSlots =
|
||||||
|
dr.mDisplayContent.getRotationReversionController().getSlotsCopy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump(String prefix, PrintWriter pw) {
|
void dump(String prefix, PrintWriter pw) {
|
||||||
@@ -2134,6 +2156,12 @@ public class DisplayRotation {
|
|||||||
if (mDisplayRotationCompatPolicySummary != null) {
|
if (mDisplayRotationCompatPolicySummary != null) {
|
||||||
pw.println(prefix + mDisplayRotationCompatPolicySummary);
|
pw.println(prefix + mDisplayRotationCompatPolicySummary);
|
||||||
}
|
}
|
||||||
|
if (mRotationReversionSlots != null) {
|
||||||
|
pw.println(prefix + " reversionSlots= NOSENSOR "
|
||||||
|
+ mRotationReversionSlots[REVERSION_TYPE_NOSENSOR] + ", CAMERA "
|
||||||
|
+ mRotationReversionSlots[REVERSION_TYPE_CAMERA_COMPAT] + " HALF_FOLD "
|
||||||
|
+ mRotationReversionSlots[REVERSION_TYPE_HALF_FOLD]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import static android.view.Display.TYPE_INTERNAL;
|
|||||||
|
|
||||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
|
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_STATES;
|
||||||
|
import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_CAMERA_COMPAT;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
@@ -156,6 +157,11 @@ final class DisplayRotationCompatPolicy {
|
|||||||
@ScreenOrientation
|
@ScreenOrientation
|
||||||
int getOrientation() {
|
int getOrientation() {
|
||||||
mLastReportedOrientation = getOrientationInternal();
|
mLastReportedOrientation = getOrientationInternal();
|
||||||
|
if (mLastReportedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
|
||||||
|
rememberOverriddenOrientationIfNeeded();
|
||||||
|
} else {
|
||||||
|
restoreOverriddenOrientationIfNeeded();
|
||||||
|
}
|
||||||
return mLastReportedOrientation;
|
return mLastReportedOrientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,6 +283,34 @@ final class DisplayRotationCompatPolicy {
|
|||||||
+ " }";
|
+ " }";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void restoreOverriddenOrientationIfNeeded() {
|
||||||
|
if (!isOrientationOverridden()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mDisplayContent.getRotationReversionController().revertOverride(
|
||||||
|
REVERSION_TYPE_CAMERA_COMPAT)) {
|
||||||
|
ProtoLog.v(WM_DEBUG_ORIENTATION,
|
||||||
|
"Reverting orientation after camera compat force rotation");
|
||||||
|
// Reset last orientation source since we have reverted the orientation.
|
||||||
|
mDisplayContent.mLastOrientationSource = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOrientationOverridden() {
|
||||||
|
return mDisplayContent.getRotationReversionController().isOverrideActive(
|
||||||
|
REVERSION_TYPE_CAMERA_COMPAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rememberOverriddenOrientationIfNeeded() {
|
||||||
|
if (!isOrientationOverridden()) {
|
||||||
|
mDisplayContent.getRotationReversionController().beforeOverrideApplied(
|
||||||
|
REVERSION_TYPE_CAMERA_COMPAT);
|
||||||
|
ProtoLog.v(WM_DEBUG_ORIENTATION,
|
||||||
|
"Saving original orientation before camera compat, last orientation is %d",
|
||||||
|
mDisplayContent.getLastOrientation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Refreshing only when configuration changes after rotation.
|
// Refreshing only when configuration changes after rotation.
|
||||||
private boolean shouldRefreshActivity(ActivityRecord activity, Configuration newConfig,
|
private boolean shouldRefreshActivity(ActivityRecord activity, Configuration newConfig,
|
||||||
Configuration lastReportedConfig) {
|
Configuration lastReportedConfig) {
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.wm;
|
||||||
|
|
||||||
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||||
|
|
||||||
|
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
|
||||||
|
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.app.WindowConfiguration;
|
||||||
|
import android.content.ActivityInfoProto;
|
||||||
|
import android.view.Surface;
|
||||||
|
|
||||||
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
|
import com.android.server.policy.WindowManagerPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the behavior of reversion from device rotation overrides.
|
||||||
|
*
|
||||||
|
* <p>There are 3 override types:
|
||||||
|
* <ol>
|
||||||
|
* <li>The top application has {@link SCREEN_ORIENTATION_NOSENSOR} set and is rotated to
|
||||||
|
* {@link ROTATION_0}.
|
||||||
|
* <li>Camera compat treatment has rotated the app {@link DisplayRotationCompatPolicy}.
|
||||||
|
* <li>The device is half-folded and has auto-rotate is temporarily enabled.
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>Before an override is enabled, a component should call {@code beforeOverrideApplied}. When
|
||||||
|
* it wishes to revert, it should call {@code revertOverride}. The user rotation will be restored
|
||||||
|
* if there are no other overrides present.
|
||||||
|
*/
|
||||||
|
final class DisplayRotationReversionController {
|
||||||
|
|
||||||
|
static final int REVERSION_TYPE_NOSENSOR = 0;
|
||||||
|
static final int REVERSION_TYPE_CAMERA_COMPAT = 1;
|
||||||
|
static final int REVERSION_TYPE_HALF_FOLD = 2;
|
||||||
|
private static final int NUM_SLOTS = 3;
|
||||||
|
|
||||||
|
@Surface.Rotation
|
||||||
|
private int mUserRotationOverridden = WindowConfiguration.ROTATION_UNDEFINED;
|
||||||
|
@WindowManagerPolicy.UserRotationMode
|
||||||
|
private int mUserRotationModeOverridden;
|
||||||
|
|
||||||
|
private final boolean[] mSlots = new boolean[NUM_SLOTS];
|
||||||
|
private final DisplayContent mDisplayContent;
|
||||||
|
|
||||||
|
DisplayRotationReversionController(DisplayContent content) {
|
||||||
|
mDisplayContent = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isRotationReversionEnabled() {
|
||||||
|
return mDisplayContent.mDisplayRotationCompatPolicy != null
|
||||||
|
|| mDisplayContent.getDisplayRotation().mFoldController != null
|
||||||
|
|| mDisplayContent.getIgnoreOrientationRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void beforeOverrideApplied(int slotIndex) {
|
||||||
|
if (mSlots[slotIndex]) return;
|
||||||
|
maybeSaveUserRotation();
|
||||||
|
mSlots[slotIndex] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isOverrideActive(int slotIndex) {
|
||||||
|
return mSlots[slotIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
boolean[] getSlotsCopy() {
|
||||||
|
return isRotationReversionEnabled() ? mSlots.clone() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateForNoSensorOverride() {
|
||||||
|
if (!mSlots[REVERSION_TYPE_NOSENSOR]) {
|
||||||
|
if (isTopFullscreenActivityNoSensor()) {
|
||||||
|
ProtoLog.v(WM_DEBUG_ORIENTATION, "NOSENSOR override detected");
|
||||||
|
beforeOverrideApplied(REVERSION_TYPE_NOSENSOR);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!isTopFullscreenActivityNoSensor()) {
|
||||||
|
ProtoLog.v(WM_DEBUG_ORIENTATION, "NOSENSOR override is absent: reverting");
|
||||||
|
revertOverride(REVERSION_TYPE_NOSENSOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAnyOverrideActive() {
|
||||||
|
for (int i = 0; i < NUM_SLOTS; ++i) {
|
||||||
|
if (mSlots[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean revertOverride(int slotIndex) {
|
||||||
|
if (!mSlots[slotIndex]) return false;
|
||||||
|
mSlots[slotIndex] = false;
|
||||||
|
if (isAnyOverrideActive()) {
|
||||||
|
ProtoLog.v(WM_DEBUG_ORIENTATION,
|
||||||
|
"Other orientation overrides are in place: not reverting");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Only override if the rotation is frozen and there are no other active slots.
|
||||||
|
if (mDisplayContent.getDisplayRotation().isRotationFrozen()) {
|
||||||
|
mDisplayContent.getDisplayRotation().setUserRotation(
|
||||||
|
mUserRotationModeOverridden,
|
||||||
|
mUserRotationOverridden);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeSaveUserRotation() {
|
||||||
|
if (!isAnyOverrideActive()) {
|
||||||
|
mUserRotationModeOverridden =
|
||||||
|
mDisplayContent.getDisplayRotation().getUserRotationMode();
|
||||||
|
mUserRotationOverridden = mDisplayContent.getDisplayRotation().getUserRotation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTopFullscreenActivityNoSensor() {
|
||||||
|
final Task topFullscreenTask =
|
||||||
|
mDisplayContent.getTask(
|
||||||
|
t -> t.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
|
||||||
|
if (topFullscreenTask != null) {
|
||||||
|
final ActivityRecord topActivity =
|
||||||
|
topFullscreenTask.topRunningActivity();
|
||||||
|
return topActivity != null && topActivity.getOrientation()
|
||||||
|
== ActivityInfoProto.SCREEN_ORIENTATION_NOSENSOR;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
|||||||
import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
|
import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
|
||||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
|
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
|
||||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
||||||
|
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_PORTRAIT;
|
||||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||||
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
|
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
|
||||||
@@ -1061,6 +1062,51 @@ public class DisplayContentTests extends WindowTestsBase {
|
|||||||
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
|
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateAllDisplayContentAndRotation(DisplayContent dc) {
|
||||||
|
// NB updateOrientation will not revert the user orientation until a settings change
|
||||||
|
// takes effect.
|
||||||
|
dc.updateOrientation();
|
||||||
|
dc.onDisplayChanged(dc);
|
||||||
|
dc.mWmService.updateRotation(true /* alwaysSendConfiguration */,
|
||||||
|
false /* forceRelayout */);
|
||||||
|
waitUntilHandlersIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoSensorRevert() {
|
||||||
|
final DisplayContent dc = mDisplayContent;
|
||||||
|
spyOn(dc);
|
||||||
|
doReturn(true).when(dc).getIgnoreOrientationRequest();
|
||||||
|
final DisplayRotation dr = dc.getDisplayRotation();
|
||||||
|
spyOn(dr);
|
||||||
|
doReturn(false).when(dr).useDefaultSettingsProvider();
|
||||||
|
final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
|
||||||
|
app.setOrientation(SCREEN_ORIENTATION_LANDSCAPE, app);
|
||||||
|
|
||||||
|
assertFalse(dc.getRotationReversionController().isAnyOverrideActive());
|
||||||
|
dc.getDisplayRotation().setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED,
|
||||||
|
ROTATION_90);
|
||||||
|
updateAllDisplayContentAndRotation(dc);
|
||||||
|
assertEquals(ROTATION_90, dc.getDisplayRotation()
|
||||||
|
.rotationForOrientation(SCREEN_ORIENTATION_UNSPECIFIED, ROTATION_90));
|
||||||
|
|
||||||
|
app.setOrientation(SCREEN_ORIENTATION_NOSENSOR);
|
||||||
|
updateAllDisplayContentAndRotation(dc);
|
||||||
|
assertTrue(dc.getRotationReversionController().isAnyOverrideActive());
|
||||||
|
assertEquals(ROTATION_0, dc.getRotation());
|
||||||
|
|
||||||
|
app.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
|
||||||
|
updateAllDisplayContentAndRotation(dc);
|
||||||
|
assertFalse(dc.getRotationReversionController().isAnyOverrideActive());
|
||||||
|
assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED,
|
||||||
|
dc.getDisplayRotation().getUserRotationMode());
|
||||||
|
assertEquals(ROTATION_90, dc.getDisplayRotation().getUserRotation());
|
||||||
|
assertEquals(ROTATION_90, dc.getDisplayRotation()
|
||||||
|
.rotationForOrientation(SCREEN_ORIENTATION_UNSPECIFIED, ROTATION_0));
|
||||||
|
dc.getDisplayRotation().setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE,
|
||||||
|
ROTATION_0);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnDescendantOrientationRequestChanged() {
|
public void testOnDescendantOrientationRequestChanged() {
|
||||||
final DisplayContent dc = createNewDisplay();
|
final DisplayContent dc = createNewDisplay();
|
||||||
|
|||||||
@@ -364,6 +364,23 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase {
|
|||||||
assertActivityRefreshRequested(/* refreshRequested */ true);
|
assertActivityRefreshRequested(/* refreshRequested */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCameraDisconnected_revertRotationAndRefresh() throws Exception {
|
||||||
|
configureActivityAndDisplay(SCREEN_ORIENTATION_PORTRAIT, ORIENTATION_LANDSCAPE);
|
||||||
|
// Open camera and test for compat treatment
|
||||||
|
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
|
||||||
|
callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
|
||||||
|
assertEquals(mDisplayRotationCompatPolicy.getOrientation(),
|
||||||
|
SCREEN_ORIENTATION_LANDSCAPE);
|
||||||
|
assertActivityRefreshRequested(/* refreshRequested */ true);
|
||||||
|
// Close camera and test for revert
|
||||||
|
mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
|
||||||
|
callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
|
||||||
|
assertEquals(mDisplayRotationCompatPolicy.getOrientation(),
|
||||||
|
SCREEN_ORIENTATION_UNSPECIFIED);
|
||||||
|
assertActivityRefreshRequested(/* refreshRequested */ true);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetOrientation_cameraConnectionClosed_returnUnspecified() {
|
public void testGetOrientation_cameraConnectionClosed_returnUnspecified() {
|
||||||
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
|
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ public class DisplayRotationTests {
|
|||||||
|
|
||||||
private static WindowManagerService sMockWm;
|
private static WindowManagerService sMockWm;
|
||||||
private DisplayContent mMockDisplayContent;
|
private DisplayContent mMockDisplayContent;
|
||||||
|
private DisplayRotationReversionController mMockDisplayRotationReversionController;
|
||||||
private DisplayPolicy mMockDisplayPolicy;
|
private DisplayPolicy mMockDisplayPolicy;
|
||||||
private DisplayAddress mMockDisplayAddress;
|
private DisplayAddress mMockDisplayAddress;
|
||||||
private Context mMockContext;
|
private Context mMockContext;
|
||||||
@@ -1367,6 +1368,10 @@ public class DisplayRotationTests {
|
|||||||
when(mMockContext.getResources().getBoolean(
|
when(mMockContext.getResources().getBoolean(
|
||||||
com.android.internal.R.bool.config_windowManagerHalfFoldAutoRotateOverride))
|
com.android.internal.R.bool.config_windowManagerHalfFoldAutoRotateOverride))
|
||||||
.thenReturn(mSupportHalfFoldAutoRotateOverride);
|
.thenReturn(mSupportHalfFoldAutoRotateOverride);
|
||||||
|
mMockDisplayRotationReversionController =
|
||||||
|
mock(DisplayRotationReversionController.class);
|
||||||
|
when(mMockDisplayContent.getRotationReversionController())
|
||||||
|
.thenReturn(mMockDisplayRotationReversionController);
|
||||||
|
|
||||||
mMockResolver = mock(ContentResolver.class);
|
mMockResolver = mock(ContentResolver.class);
|
||||||
when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
|
when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
|
||||||
|
|||||||
Reference in New Issue
Block a user