diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 602ee556204c8..d3aee4329dd2a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -974,6 +974,12 @@
true
+
+
+
+
+
+
diff --git a/services/core/java/com/android/server/wm/DeviceStateController.java b/services/core/java/com/android/server/wm/DeviceStateController.java
index 2e67399321a00..7d9a4ec4b5c33 100644
--- a/services/core/java/com/android/server/wm/DeviceStateController.java
+++ b/services/core/java/com/android/server/wm/DeviceStateController.java
@@ -47,10 +47,13 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb
@NonNull
private final int[] mRearDisplayDeviceStates;
@NonNull
+ private final int[] mReverseRotationAroundZAxisStates;
+ @NonNull
private final List> mDeviceStateCallbacks = new ArrayList<>();
@Nullable
private DeviceState mLastDeviceState;
+ private int mCurrentState;
public enum DeviceState {
UNKNOWN, OPEN, FOLDED, HALF_FOLDED, REAR,
@@ -58,6 +61,7 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb
DeviceStateController(@NonNull Context context, @NonNull Handler handler) {
mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
+
mOpenDeviceStates = context.getResources()
.getIntArray(R.array.config_openDeviceStates);
mHalfFoldedDeviceStates = context.getResources()
@@ -66,6 +70,8 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb
.getIntArray(R.array.config_foldedDeviceStates);
mRearDisplayDeviceStates = context.getResources()
.getIntArray(R.array.config_rearDisplayDeviceStates);
+ mReverseRotationAroundZAxisStates = context.getResources()
+ .getIntArray(R.array.config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis);
if (mDeviceStateManager != null) {
mDeviceStateManager.registerCallback(new HandlerExecutor(handler), this);
@@ -82,8 +88,17 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb
mDeviceStateCallbacks.add(callback);
}
+ /**
+ * @return true if the rotation direction on the Z axis should be reversed.
+ */
+ boolean shouldReverseRotationDirectionAroundZAxis() {
+ return ArrayUtils.contains(mReverseRotationAroundZAxisStates, mCurrentState);
+ }
+
@Override
public void onStateChanged(int state) {
+ mCurrentState = state;
+
final DeviceState deviceState;
if (ArrayUtils.contains(mHalfFoldedDeviceStates, state)) {
deviceState = DeviceState.HALF_FOLDED;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9581ffe0f8b97..acbefce6fcc28 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1148,7 +1148,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH);
mDisplayPolicy = new DisplayPolicy(mWmService, this);
- mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address);
+ mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address,
+ mDeviceStateController);
final Consumer deviceStateConsumer =
(@NonNull DeviceStateController.DeviceState newFoldState) -> {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 762f49a7e23d4..6f7ff5c21d94c 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -41,6 +41,7 @@ import static com.android.server.wm.WindowManagerService.WINDOW_FREEZE_TIMEOUT_D
import android.annotation.AnimRes;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ContentResolver;
@@ -117,6 +118,8 @@ public class DisplayRotation {
private SettingsObserver mSettingsObserver;
@Nullable
private FoldController mFoldController;
+ @NonNull
+ private final DeviceStateController mDeviceStateController;
@ScreenOrientation
private int mCurrentAppOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
@@ -218,21 +221,24 @@ public class DisplayRotation {
private boolean mDemoRotationLock;
DisplayRotation(WindowManagerService service, DisplayContent displayContent,
- DisplayAddress displayAddress) {
+ DisplayAddress displayAddress, @NonNull DeviceStateController deviceStateController) {
this(service, displayContent, displayAddress, displayContent.getDisplayPolicy(),
- service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock());
+ service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock(),
+ deviceStateController);
}
@VisibleForTesting
DisplayRotation(WindowManagerService service, DisplayContent displayContent,
DisplayAddress displayAddress, DisplayPolicy displayPolicy,
- DisplayWindowSettings displayWindowSettings, Context context, Object lock) {
+ DisplayWindowSettings displayWindowSettings, Context context, Object lock,
+ @NonNull DeviceStateController deviceStateController) {
mService = service;
mDisplayContent = displayContent;
mDisplayPolicy = displayPolicy;
mDisplayWindowSettings = displayWindowSettings;
mContext = context;
mLock = lock;
+ mDeviceStateController = deviceStateController;
isDefaultDisplay = displayContent.isDefaultDisplay;
mCompatPolicyForImmersiveApps = initImmersiveAppCompatPolicy(service, displayContent);
@@ -1137,6 +1143,15 @@ public class DisplayRotation {
int sensorRotation = mOrientationListener != null
? mOrientationListener.getProposedRotation() // may be -1
: -1;
+ if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()) {
+ // Flipping 270 and 90 has the same effect as changing the direction which rotation is
+ // applied.
+ if (sensorRotation == Surface.ROTATION_90) {
+ sensorRotation = Surface.ROTATION_270;
+ } else if (sensorRotation == Surface.ROTATION_270) {
+ sensorRotation = Surface.ROTATION_90;
+ }
+ }
mLastSensorRotation = sensorRotation;
if (sensorRotation < 0) {
sensorRotation = lastRotation;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index f814608ed87ae..ed2b0a36cd5c8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -56,6 +56,7 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceStateManager;
import android.os.PowerManagerInternal;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
@@ -111,6 +112,7 @@ public class DisplayRotationTests {
private ContentResolver mMockResolver;
private FakeSettingsProvider mFakeSettingsProvider;
private StatusBarManagerInternal mMockStatusBarManagerInternal;
+ private DeviceStateManager mMockDeviceStateManager;
// Fields below are callbacks captured from test target.
private ContentObserver mShowRotationSuggestionsObserver;
@@ -120,6 +122,7 @@ public class DisplayRotationTests {
private DisplayRotationBuilder mBuilder;
+ private DeviceStateController mDeviceStateController;
private DisplayRotation mTarget;
@BeforeClass
@@ -484,6 +487,34 @@ public class DisplayRotationTests {
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
}
+ @Test
+ public void testReverseRotation() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ when(mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()).thenReturn(true);
+
+ thawRotation();
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_270));
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+ assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_180));
+ }
+
private boolean waitForUiHandler() {
final CountDownLatch latch = new CountDownLatch(1);
UiThread.getHandler().post(latch::countDown);
@@ -1097,8 +1128,14 @@ public class DisplayRotationTests {
mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
+ mMockDeviceStateManager = mock(DeviceStateManager.class);
+ when(mMockContext.getSystemService(eq(DeviceStateManager.class)))
+ .thenReturn(mMockDeviceStateManager);
+
+ mDeviceStateController = mock(DeviceStateController.class);
mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayAddress,
- mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext, new Object()) {
+ mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext, new Object(),
+ mDeviceStateController) {
@Override
DisplayRotationImmersiveAppCompatPolicy initImmersiveAppCompatPolicy(
WindowManagerService service, DisplayContent displayContent) {