diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 92d23327e6c40..514083cefca9b 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -696,6 +696,20 @@ rotation. --> false + + false + + + 0 + + + 0 + + + 0 + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index d8b261c50cb10..17a8a76bd5903 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4036,6 +4036,10 @@ + + + + diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ec355b73a7be4..fe1389ae5d6cb 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2885,6 +2885,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /* 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. @@ -3262,6 +3263,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp 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 e9569d8cc0641..4ecda122ff341 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -52,8 +52,13 @@ import android.content.pm.ActivityInfo.ScreenOrientation; 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; @@ -1054,6 +1059,10 @@ public class DisplayRotation { return false; } + if (mFoldController != null && mFoldController.shouldDisableRotationSensor()) { + return false; + } + if (mSupportAutoRotation) { if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR @@ -1151,6 +1160,9 @@ public class DisplayRotation { int sensorRotation = mOrientationListener != null ? mOrientationListener.getProposedRotation() // may be -1 : -1; + if (mFoldController != null && mFoldController.shouldIgnoreSensorRotation()) { + sensorRotation = -1; + } if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()) { // Flipping 270 and 90 has the same effect as changing the direction which rotation is // applied. @@ -1398,6 +1410,11 @@ public class DisplayRotation { 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; @@ -1491,6 +1508,12 @@ public class DisplayRotation { } } + void onDisplayRemoved() { + if (mFoldController != null) { + mFoldController.onDisplayRemoved(); + } + } + /** Return whether the rotation settings has changed. */ private boolean updateSettings() { final ContentResolver resolver = mContext.getContentResolver(); @@ -1580,6 +1603,22 @@ public class DisplayRotation { 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"); @@ -1621,13 +1660,37 @@ public class DisplayRotation { } } - 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 mTabletopRotations; private final Runnable mActivityBoundsUpdateCallback; @@ -1684,6 +1747,48 @@ public class DisplayRotation { } } }; + + 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) { @@ -1713,6 +1818,7 @@ public class DisplayRotation { 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 @@ -1759,6 +1865,80 @@ public class DisplayRotation { 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 26d2a46f768ad..50c717a58ec66 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -61,6 +61,7 @@ import android.hardware.SensorManager; import android.hardware.devicestate.DeviceStateManager; 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; @@ -74,6 +75,9 @@ import com.android.server.LocalServices; 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; @@ -101,6 +105,9 @@ public class DisplayRotationTests { 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; @@ -110,6 +117,7 @@ public class DisplayRotationTests { private Resources mMockRes; private SensorManager mMockSensorManager; private Sensor mFakeOrientationSensor; + private Sensor mFakeHingeAngleSensor; private DisplayWindowSettings mMockDisplayWindowSettings; private ContentResolver mMockResolver; private FakeSettingsProvider mFakeSettingsProvider; @@ -122,6 +130,9 @@ public class DisplayRotationTests { private ContentObserver mUserRotationObserver; private SensorEventListener mOrientationSensorListener; + ArgumentCaptor mHingeAngleSensorListenerCaptor = ArgumentCaptor.forClass( + SensorEventListener.class); + private DisplayRotationBuilder mBuilder; private DeviceStateController mDeviceStateController; @@ -132,6 +143,7 @@ public class DisplayRotationTests { sMockWm = mock(WindowManagerService.class); sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class); sMockWm.mPolicy = mock(WindowManagerPolicy.class); + sHandler = new TestHandler(null, sClock); } @AfterClass @@ -464,12 +476,16 @@ public class DisplayRotationTests { } private SensorEvent createSensorEvent(int rotation) throws Exception { + return createSensorEvent(mFakeOrientationSensor, rotation); + } + + private SensorEvent createSensorEvent(Sensor sensor, int value) throws Exception { final Constructor 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; } @@ -901,6 +917,120 @@ public class DisplayRotationTests { 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 // ======================== @@ -921,6 +1051,12 @@ public class DisplayRotationTests { + " 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. @@ -953,6 +1089,17 @@ public class DisplayRotationTests { 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); @@ -974,7 +1121,11 @@ public class DisplayRotationTests { 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; @@ -986,6 +1137,29 @@ public class DisplayRotationTests { 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; @@ -1111,10 +1285,27 @@ public class DisplayRotationTests { 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)) @@ -1127,11 +1318,16 @@ public class DisplayRotationTests { .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)) @@ -1160,10 +1356,24 @@ public class DisplayRotationTests { WindowManagerService service, DisplayContent displayContent) { return null; } + + @Override + Handler getHandler() { + return sHandler; + } + + @Override + long uptimeMillis() { + return sCurrentUptimeMillis; + } }; reset(sMockWm); + verify(mMockSensorManager, atLeast(0)).registerListener( + mHingeAngleSensorListenerCaptor.capture(), eq(mFakeHingeAngleSensor), anyInt(), + any()); + captureObservers(); } }