Prevent autorotation during hinge movement
Pauses autorotation when hinge angle sensor
reports values and display switch.
This reduces accidental rotation while
folding or unfolding a foldable device.
Bug: 233055896
Test: atest WmTests:DisplayRotationTests
Change-Id: I1b07e1e84ab1cafb0f2794d62d99e114908fe840
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ef19fc1..639e54b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -680,6 +680,20 @@
rotation. -->
<bool name="config_windowManagerHalfFoldAutoRotateOverride">false</bool>
+ <!-- Indicates whether the window manager pauses autorotation when folding or unfolding
+ a foldable device based on hinge angle sensor events and physical display switch events. -->
+ <bool name="config_windowManagerPauseRotationWhenUnfolding">false</bool>
+
+ <!-- Amount of time during which autorotation will be disabled since last hinge angle event -->
+ <integer name="config_pauseRotationWhenUnfolding_maxHingeAngle">0</integer>
+
+ <!-- Maximum hinge angle event to be considered to disable autorotation when folding or
+ unfolding -->
+ <integer name="config_pauseRotationWhenUnfolding_hingeEventTimeout">0</integer>
+
+ <!-- Amount of time during which autorotation will be disabled since last display switch -->
+ <integer name="config_pauseRotationWhenUnfolding_displaySwitchTimeout">0</integer>
+
<!-- When a device enters any of these states, it should be woken up. States are defined in
device_state_configuration.xml. -->
<integer-array name="config_deviceStatesOnWhichToWakeUp">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8855d5b..5022e32 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4017,6 +4017,10 @@
<java-symbol type="array" name="config_halfFoldedDeviceStates" />
<java-symbol type="array" name="config_rearDisplayDeviceStates" />
<java-symbol type="bool" name="config_windowManagerHalfFoldAutoRotateOverride" />
+ <java-symbol type="bool" name="config_windowManagerPauseRotationWhenUnfolding" />
+ <java-symbol type="integer" name="config_pauseRotationWhenUnfolding_hingeEventTimeout" />
+ <java-symbol type="integer" name="config_pauseRotationWhenUnfolding_maxHingeAngle" />
+ <java-symbol type="integer" name="config_pauseRotationWhenUnfolding_displaySwitchTimeout" />
<java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
<java-symbol type="array" name="config_deviceStatesOnWhichToSleep" />
<java-symbol type="string" name="config_foldedArea" />
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 89cb13a..ab109fca 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2921,6 +2921,7 @@
/* includeRotationSettings */ false);
mDisplaySwitchTransitionLauncher.requestDisplaySwitchTransitionIfNeeded(mDisplayId,
mInitialDisplayWidth, mInitialDisplayHeight, newWidth, newHeight);
+ mDisplayRotation.physicalDisplayChanged();
}
// If there is an override set for base values - use it, otherwise use new values.
@@ -3291,7 +3292,6 @@
mTransitionController.unregisterLegacyListener(mFixedRotationTransitionListener);
handleAnimatingStoppedAndTransition();
mWmService.stopFreezingDisplayLocked();
- mDisplayRotation.removeDefaultDisplayRotationChangedCallback();
mDeviceStateController.unregisterDeviceStateCallback(mDeviceStateConsumer);
super.removeImmediately();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
@@ -3305,6 +3305,7 @@
mWindowingLayer.release();
mInputMonitor.onDisplayRemoved();
mWmService.mDisplayNotificationController.dispatchDisplayRemoved(this);
+ mDisplayRotation.onDisplayRemoved();
mWmService.mAccessibilityController.onDisplayRemoved(mDisplayId);
mRootWindowContainer.mTaskSupervisor
.getKeyguardController().onDisplayRemoved(mDisplayId);
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 6af1c7c9..628f4d3 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -51,8 +51,13 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
import android.hardware.power.Boost;
import android.os.Handler;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
@@ -1085,6 +1090,10 @@
return false;
}
+ if (mFoldController != null && mFoldController.shouldDisableRotationSensor()) {
+ return false;
+ }
+
if (mSupportAutoRotation) {
if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
|| mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
@@ -1183,6 +1192,9 @@
int sensorRotation = mOrientationListener != null
? mOrientationListener.getProposedRotation() // may be -1
: -1;
+ if (mFoldController != null && mFoldController.shouldIgnoreSensorRotation()) {
+ sensorRotation = -1;
+ }
if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()) {
sensorRotation = RotationUtils.reverseRotationDirectionAroundZAxis(sensorRotation);
}
@@ -1425,6 +1437,11 @@
return false;
}
+ // Do not show rotation choice when fold controller blocks rotation sensor
+ if (mFoldController != null && mFoldController.shouldIgnoreSensorRotation()) {
+ return false;
+ }
+
// Don't show rotation choice if we are in tabletop or book modes.
if (isTabletopAutoRotateOverrideEnabled()) return false;
@@ -1527,6 +1544,13 @@
}
}
+ void onDisplayRemoved() {
+ removeDefaultDisplayRotationChangedCallback();
+ if (mFoldController != null) {
+ mFoldController.onDisplayRemoved();
+ }
+ }
+
/** Return whether the rotation settings has changed. */
private boolean updateSettings() {
final ContentResolver resolver = mContext.getContentResolver();
@@ -1622,6 +1646,22 @@
pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
pw.println(prefix + " mFixedToUserRotation=" + isFixedToUserRotation());
+ if (mFoldController != null) {
+ pw.println(prefix + "FoldController");
+ pw.println(prefix + " mPauseAutorotationDuringUnfolding="
+ + mFoldController.mPauseAutorotationDuringUnfolding);
+ pw.println(prefix + " mShouldDisableRotationSensor="
+ + mFoldController.mShouldDisableRotationSensor);
+ pw.println(prefix + " mShouldIgnoreSensorRotation="
+ + mFoldController.mShouldIgnoreSensorRotation);
+ pw.println(prefix + " mLastDisplaySwitchTime="
+ + mFoldController.mLastDisplaySwitchTime);
+ pw.println(prefix + " mLastHingeAngleEventTime="
+ + mFoldController.mLastHingeAngleEventTime);
+ pw.println(prefix + " mDeviceState="
+ + mFoldController.mDeviceState);
+ }
+
if (!mRotationHistory.mRecords.isEmpty()) {
pw.println();
pw.println(prefix + " RotationHistory");
@@ -1663,13 +1703,37 @@
}
}
- private class FoldController {
+ /**
+ * Called by the DisplayContent when the physical display changes
+ */
+ void physicalDisplayChanged() {
+ if (mFoldController != null) {
+ mFoldController.onPhysicalDisplayChanged();
+ }
+ }
+
+ @VisibleForTesting
+ long uptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
+
+ class FoldController {
+ private final boolean mPauseAutorotationDuringUnfolding;
@Surface.Rotation
private int mHalfFoldSavedRotation = -1; // No saved rotation
private DeviceStateController.DeviceState mDeviceState =
DeviceStateController.DeviceState.UNKNOWN;
+ private long mLastHingeAngleEventTime = 0;
+ private long mLastDisplaySwitchTime = 0;
+ private boolean mShouldIgnoreSensorRotation;
+ private boolean mShouldDisableRotationSensor;
private boolean mInHalfFoldTransition = false;
+ private int mDisplaySwitchRotationBlockTimeMs;
+ private int mHingeAngleRotationBlockTimeMs;
+ private int mMaxHingeAngle;
private final boolean mIsDisplayAlwaysSeparatingHinge;
+ private SensorManager mSensorManager;
+ private SensorEventListener mHingeAngleSensorEventListener;
private final Set<Integer> mTabletopRotations;
private final Runnable mActivityBoundsUpdateCallback;
@@ -1726,6 +1790,48 @@
}
}
};
+
+ mPauseAutorotationDuringUnfolding = mContext.getResources().getBoolean(
+ R.bool.config_windowManagerPauseRotationWhenUnfolding);
+
+ if (mPauseAutorotationDuringUnfolding) {
+ mDisplaySwitchRotationBlockTimeMs = mContext.getResources().getInteger(
+ R.integer.config_pauseRotationWhenUnfolding_displaySwitchTimeout);
+ mHingeAngleRotationBlockTimeMs = mContext.getResources().getInteger(
+ R.integer.config_pauseRotationWhenUnfolding_hingeEventTimeout);
+ mMaxHingeAngle = mContext.getResources().getInteger(
+ R.integer.config_pauseRotationWhenUnfolding_maxHingeAngle);
+ registerSensorManager();
+ }
+ }
+
+ private void registerSensorManager() {
+ mSensorManager = mContext.getSystemService(SensorManager.class);
+ if (mSensorManager != null) {
+ final Sensor hingeAngleSensor = mSensorManager
+ .getDefaultSensor(Sensor.TYPE_HINGE_ANGLE);
+
+ if (hingeAngleSensor != null) {
+ mHingeAngleSensorEventListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ onHingeAngleChanged(event.values[0]);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ };
+ mSensorManager.registerListener(mHingeAngleSensorEventListener,
+ hingeAngleSensor, SensorManager.SENSOR_DELAY_FASTEST, getHandler());
+ }
+ }
+ }
+
+ void onDisplayRemoved() {
+ if (mSensorManager != null && mHingeAngleSensorEventListener != null) {
+ mSensorManager.unregisterListener(mHingeAngleSensorEventListener);
+ }
}
boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
@@ -1755,6 +1861,7 @@
boolean shouldRevertOverriddenRotation() {
// When transitioning to open.
return mDeviceState == DeviceStateController.DeviceState.OPEN
+ && !mShouldIgnoreSensorRotation // Ignore if the hinge angle still moving
&& mInHalfFoldTransition
&& mHalfFoldSavedRotation != -1 // Ignore if we've already reverted.
&& mUserRotationMode
@@ -1801,6 +1908,80 @@
UiThread.getHandler().postDelayed(mActivityBoundsUpdateCallback,
FOLDING_RECOMPUTE_CONFIG_DELAY_MS);
}
+
+ boolean shouldIgnoreSensorRotation() {
+ return mShouldIgnoreSensorRotation;
+ }
+
+ boolean shouldDisableRotationSensor() {
+ return mShouldDisableRotationSensor;
+ }
+
+ private void updateSensorRotationBlockIfNeeded() {
+ final long currentTime = uptimeMillis();
+ final boolean newShouldIgnoreRotation =
+ currentTime - mLastDisplaySwitchTime < mDisplaySwitchRotationBlockTimeMs
+ || currentTime - mLastHingeAngleEventTime < mHingeAngleRotationBlockTimeMs;
+
+ if (newShouldIgnoreRotation != mShouldIgnoreSensorRotation) {
+ mShouldIgnoreSensorRotation = newShouldIgnoreRotation;
+
+ // Resuming the autorotation
+ if (!mShouldIgnoreSensorRotation) {
+ if (mShouldDisableRotationSensor) {
+ // Sensor was disabled, let's re-enable it
+ mShouldDisableRotationSensor = false;
+ updateOrientationListenerLw();
+ } else {
+ // Sensor was not disabled, let's update the rotation in case if we received
+ // some rotation sensor updates when autorotate was disabled
+ updateRotationAndSendNewConfigIfChanged();
+ }
+ }
+ }
+ }
+
+ void onPhysicalDisplayChanged() {
+ if (!mPauseAutorotationDuringUnfolding) return;
+
+ mLastDisplaySwitchTime = uptimeMillis();
+
+ final boolean isUnfolding =
+ mDeviceState == DeviceStateController.DeviceState.OPEN
+ || mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;
+
+ if (isUnfolding) {
+ // Temporary disable rotation sensor updates when unfolding
+ mShouldDisableRotationSensor = true;
+ updateOrientationListenerLw();
+ }
+
+ updateSensorRotationBlockIfNeeded();
+ getHandler().postDelayed(() -> {
+ synchronized (mLock) {
+ updateSensorRotationBlockIfNeeded();
+ };
+ }, mDisplaySwitchRotationBlockTimeMs);
+ }
+
+ void onHingeAngleChanged(float hingeAngle) {
+ if (hingeAngle < mMaxHingeAngle) {
+ mLastHingeAngleEventTime = uptimeMillis();
+
+ updateSensorRotationBlockIfNeeded();
+
+ getHandler().postDelayed(() -> {
+ synchronized (mLock) {
+ updateSensorRotationBlockIfNeeded();
+ };
+ }, mHingeAngleRotationBlockTimeMs);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ Handler getHandler() {
+ return mService.mH;
}
private class OrientationListener extends WindowOrientationListener implements Runnable {
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 146ed34..495f868 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -62,6 +62,7 @@
import android.os.IBinder;
import android.os.PowerManagerInternal;
import android.os.SystemClock;
+import android.os.Handler;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.view.DisplayAddress;
@@ -76,6 +77,9 @@
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+import com.android.server.wm.DisplayContent.FixedRotationTransitionListener;
import org.junit.After;
import org.junit.AfterClass;
@@ -104,6 +108,9 @@
private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50;
private StatusBarManagerInternal mPreviousStatusBarManagerInternal;
+ private static final OffsettableClock sClock = new OffsettableClock.Stopped();
+ private static TestHandler sHandler;
+ private static long sCurrentUptimeMillis = 10_000;
private static WindowManagerService sMockWm;
private DisplayContent mMockDisplayContent;
@@ -113,6 +120,7 @@
private Resources mMockRes;
private SensorManager mMockSensorManager;
private Sensor mFakeOrientationSensor;
+ private Sensor mFakeHingeAngleSensor;
private DisplayWindowSettings mMockDisplayWindowSettings;
private ContentResolver mMockResolver;
private FakeSettingsProvider mFakeSettingsProvider;
@@ -125,6 +133,9 @@
private ContentObserver mUserRotationObserver;
private SensorEventListener mOrientationSensorListener;
+ ArgumentCaptor<SensorEventListener> mHingeAngleSensorListenerCaptor = ArgumentCaptor.forClass(
+ SensorEventListener.class);
+
private DisplayRotationBuilder mBuilder;
private DeviceStateController mDeviceStateController;
@@ -135,6 +146,7 @@
sMockWm = mock(WindowManagerService.class);
sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
sMockWm.mPolicy = mock(WindowManagerPolicy.class);
+ sHandler = new TestHandler(null, sClock);
}
@AfterClass
@@ -468,12 +480,16 @@
}
private SensorEvent createSensorEvent(int rotation) throws Exception {
+ return createSensorEvent(mFakeOrientationSensor, rotation);
+ }
+
+ private SensorEvent createSensorEvent(Sensor sensor, int value) throws Exception {
final Constructor<SensorEvent> constructor =
SensorEvent.class.getDeclaredConstructor(int.class);
constructor.setAccessible(true);
final SensorEvent event = constructor.newInstance(1);
- event.sensor = mFakeOrientationSensor;
- event.values[0] = rotation;
+ event.sensor = sensor;
+ event.values[0] = value;
event.timestamp = SystemClock.elapsedRealtimeNanos();
return event;
}
@@ -943,6 +959,120 @@
Surface.ROTATION_0, Surface.ROTATION_90, false /* forceUpdate */));
}
+ @Test
+ public void testSensorRotationAfterDisplayChangeBeforeTimeout_ignoresSensor() throws Exception {
+ mBuilder.setSupportHalfFoldAutoRotateOverride(true)
+ .setPauseRotationWhenUnfolding(true)
+ .setDisplaySwitchRotationBlockTimeMs(1000)
+ .build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+ thawRotation();
+ enableOrientationSensor();
+
+ mTarget.physicalDisplayChanged();
+
+ moveTimeForward(900);
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ @Test
+ public void testSensorRotationAfterDisplayChangeAfterTimeout_usesSensor() throws Exception {
+ mBuilder.setSupportHalfFoldAutoRotateOverride(true)
+ .setPauseRotationWhenUnfolding(true)
+ .setDisplaySwitchRotationBlockTimeMs(1000)
+ .build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+ thawRotation();
+ enableOrientationSensor();
+
+ mTarget.physicalDisplayChanged();
+
+ moveTimeForward(1100);
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ @Test
+ public void testSensorRotationAfterHingeEventBeforeTimeout_ignoresSensor() throws Exception {
+ mBuilder.setSupportHalfFoldAutoRotateOverride(true)
+ .setPauseRotationWhenUnfolding(true)
+ .setMaxHingeAngle(165)
+ .setHingeAngleRotationBlockTimeMs(400)
+ .build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+ thawRotation();
+ enableOrientationSensor();
+
+ sendHingeAngleEvent(130);
+
+ moveTimeForward( 300);
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ @Test
+ public void testSensorRotationAfterHingeEventBeforeTimeoutFlagDisabled_usesSensorData()
+ throws Exception {
+ mBuilder.setSupportHalfFoldAutoRotateOverride(true)
+ .setPauseRotationWhenUnfolding(false)
+ .setMaxHingeAngle(165)
+ .setHingeAngleRotationBlockTimeMs(400)
+ .build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+ thawRotation();
+ enableOrientationSensor();
+
+ sendHingeAngleEvent(130);
+
+ moveTimeForward( 300);
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ @Test
+ public void testSensorRotationAfterHingeEventAfterTimeout_usesSensorData() throws Exception {
+ mBuilder.setSupportHalfFoldAutoRotateOverride(true)
+ .setPauseRotationWhenUnfolding(true)
+ .setMaxHingeAngle(165)
+ .setHingeAngleRotationBlockTimeMs(400)
+ .build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+ thawRotation();
+ enableOrientationSensor();
+
+ sendHingeAngleEvent(180);
+
+ moveTimeForward(1010);
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+
+ @Test
+ public void testSensorRotationAfterLargeHingeEventBeforeTimeout_usesSensor() throws Exception {
+ mBuilder.setSupportHalfFoldAutoRotateOverride(true)
+ .setPauseRotationWhenUnfolding(true)
+ .setMaxHingeAngle(165)
+ .setHingeAngleRotationBlockTimeMs(400)
+ .build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+ thawRotation();
+ enableOrientationSensor();
+
+ sendHingeAngleEvent(180);
+
+ moveTimeForward(300);
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
// ========================
// Non-rotation API Tests
// ========================
@@ -963,6 +1093,12 @@
+ " fixed to user rotation.", mTarget.isFixedToUserRotation());
}
+ private void moveTimeForward(long timeMillis) {
+ sCurrentUptimeMillis += timeMillis;
+ sClock.fastForward(timeMillis);
+ sHandler.timeAdvance();
+ }
+
/**
* Call {@link DisplayRotation#configure(int, int)} to configure {@link #mTarget}
* according to given parameters.
@@ -995,6 +1131,17 @@
mTarget.configure(width, height);
}
+ private void sendHingeAngleEvent(int hingeAngle) {
+ mHingeAngleSensorListenerCaptor.getAllValues().forEach(sensorEventListener -> {
+ try {
+ sensorEventListener.onSensorChanged(createSensorEvent(mFakeHingeAngleSensor,
+ hingeAngle));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
private void freezeRotation(int rotation) {
mTarget.freezeRotation(rotation);
@@ -1016,7 +1163,11 @@
private class DisplayRotationBuilder {
private boolean mIsDefaultDisplay = true;
private boolean mSupportAutoRotation = true;
+ private boolean mPauseRotationWhenUnfolding = false;
private boolean mSupportHalfFoldAutoRotateOverride = false;
+ private int mDisplaySwitchRotationBlockTimeMs;
+ private int mHingeAngleRotationBlockTimeMs;
+ private int mMaxHingeAngle;
private int mLidOpenRotation = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
private int mCarDockRotation;
@@ -1028,6 +1179,29 @@
return this;
}
+ public DisplayRotationBuilder setPauseRotationWhenUnfolding(
+ boolean pauseRotationWhenUnfolding) {
+ mPauseRotationWhenUnfolding = pauseRotationWhenUnfolding;
+ return this;
+ }
+
+ public DisplayRotationBuilder setDisplaySwitchRotationBlockTimeMs(
+ int displaySwitchRotationBlockTimeMs) {
+ mDisplaySwitchRotationBlockTimeMs = displaySwitchRotationBlockTimeMs;
+ return this;
+ }
+
+ public DisplayRotationBuilder setHingeAngleRotationBlockTimeMs(
+ int hingeAngleRotationBlockTimeMs) {
+ mHingeAngleRotationBlockTimeMs = hingeAngleRotationBlockTimeMs;
+ return this;
+ }
+
+ public DisplayRotationBuilder setMaxHingeAngle(int maxHingeAngle) {
+ mMaxHingeAngle = maxHingeAngle;
+ return this;
+ }
+
private DisplayRotationBuilder setSupportAutoRotation(boolean supportAutoRotation) {
mSupportAutoRotation = supportAutoRotation;
return this;
@@ -1153,10 +1327,27 @@
when(mMockDisplayContent.getWindowConfiguration())
.thenReturn(new WindowConfiguration());
+ Field field = DisplayContent.class
+ .getDeclaredField("mFixedRotationTransitionListener");
+ field.setAccessible(true);
+ field.set(mMockDisplayContent, mock(FixedRotationTransitionListener.class));
+
mMockDisplayPolicy = mock(DisplayPolicy.class);
mMockRes = mock(Resources.class);
when(mMockContext.getResources()).thenReturn((mMockRes));
+ when(mMockRes.getBoolean(com.android.internal.R.bool
+ .config_windowManagerPauseRotationWhenUnfolding))
+ .thenReturn(mPauseRotationWhenUnfolding);
+ when(mMockRes.getInteger(com.android.internal.R.integer
+ .config_pauseRotationWhenUnfolding_displaySwitchTimeout))
+ .thenReturn(mDisplaySwitchRotationBlockTimeMs);
+ when(mMockRes.getInteger(com.android.internal.R.integer
+ .config_pauseRotationWhenUnfolding_hingeEventTimeout))
+ .thenReturn(mHingeAngleRotationBlockTimeMs);
+ when(mMockRes.getInteger(com.android.internal.R.integer
+ .config_pauseRotationWhenUnfolding_maxHingeAngle))
+ .thenReturn(mMaxHingeAngle);
when(mMockRes.getBoolean(com.android.internal.R.bool.config_supportAutoRotation))
.thenReturn(mSupportAutoRotation);
when(mMockRes.getInteger(com.android.internal.R.integer.config_lidOpenRotation))
@@ -1169,11 +1360,16 @@
.thenReturn(convertRotationToDegrees(mUndockedHdmiRotation));
mMockSensorManager = mock(SensorManager.class);
+ when(mMockContext.getSystemService(SensorManager.class))
+ .thenReturn(mMockSensorManager);
when(mMockContext.getSystemService(Context.SENSOR_SERVICE))
.thenReturn(mMockSensorManager);
mFakeOrientationSensor = createSensor(Sensor.TYPE_DEVICE_ORIENTATION);
when(mMockSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION)).thenReturn(
Collections.singletonList(mFakeOrientationSensor));
+ mFakeHingeAngleSensor = mock(Sensor.class);
+ when(mMockSensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)).thenReturn(
+ mFakeHingeAngleSensor);
when(mMockContext.getResources().getBoolean(
com.android.internal.R.bool.config_windowManagerHalfFoldAutoRotateOverride))
@@ -1200,6 +1396,10 @@
reset(sMockWm);
+ verify(mMockSensorManager, atLeast(0)).registerListener(
+ mHingeAngleSensorListenerCaptor.capture(), eq(mFakeHingeAngleSensor), anyInt(),
+ any());
+
captureObservers();
}
}
@@ -1226,5 +1426,15 @@
mProposedRotationCallback.accept(rotation);
}
}
+
+ @Override
+ Handler getHandler() {
+ return sHandler;
+ }
+
+ @Override
+ long uptimeMillis() {
+ return sCurrentUptimeMillis;
+ }
}
}