blob: 495f868b1b114aa39de6bcd7c57ea681b17270df [file] [log] [blame]
/*
* Copyright (C) 2018 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.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_SENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.DisplayCutout.NO_CUTOUT;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DISABLED;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atMost;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.clearInvocations;
import android.app.WindowConfiguration;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
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.devicestate.DeviceStateManager;
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;
import android.view.IRotationWatcher;
import android.view.Surface;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import com.android.internal.util.test.FakeSettingsProvider;
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;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.IntConsumer;
/**
* Test class for {@link DisplayRotation}.
*
* Build/Install/Run:
* atest WmTests:DisplayRotationTests
*/
@SmallTest
@Presubmit
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;
private DisplayPolicy mMockDisplayPolicy;
private DisplayAddress mMockDisplayAddress;
private Context mMockContext;
private Resources mMockRes;
private SensorManager mMockSensorManager;
private Sensor mFakeOrientationSensor;
private Sensor mFakeHingeAngleSensor;
private DisplayWindowSettings mMockDisplayWindowSettings;
private ContentResolver mMockResolver;
private FakeSettingsProvider mFakeSettingsProvider;
private StatusBarManagerInternal mMockStatusBarManagerInternal;
private DeviceStateManager mMockDeviceStateManager;
// Fields below are callbacks captured from test target.
private ContentObserver mShowRotationSuggestionsObserver;
private ContentObserver mAccelerometerRotationObserver;
private ContentObserver mUserRotationObserver;
private SensorEventListener mOrientationSensorListener;
ArgumentCaptor<SensorEventListener> mHingeAngleSensorListenerCaptor = ArgumentCaptor.forClass(
SensorEventListener.class);
private DisplayRotationBuilder mBuilder;
private DeviceStateController mDeviceStateController;
private TestDisplayRotation mTarget;
@BeforeClass
public static void setUpOnce() {
sMockWm = mock(WindowManagerService.class);
sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
sMockWm.mPolicy = mock(WindowManagerPolicy.class);
sHandler = new TestHandler(null, sClock);
}
@AfterClass
public static void tearDownOnce() {
sMockWm = null;
// Make sure the fake settings are cleared after the last test method.
FakeSettingsProvider.clearSettingsProvider();
}
@Before
public void setUp() {
FakeSettingsProvider.clearSettingsProvider();
mPreviousStatusBarManagerInternal = LocalServices.getService(
StatusBarManagerInternal.class);
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class);
LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
mBuilder = new DisplayRotationBuilder();
}
@After
public void tearDown() {
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
if (mPreviousStatusBarManagerInternal != null) {
LocalServices.addService(StatusBarManagerInternal.class,
mPreviousStatusBarManagerInternal);
mPreviousStatusBarManagerInternal = null;
}
}
// ================================
// Display Settings Related Tests
// ================================
@Test
public void testLocksUserRotation_LockRotation_DefaultDisplay() throws Exception {
mBuilder.build();
freezeRotation(Surface.ROTATION_180);
assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode());
assertEquals(Surface.ROTATION_180, mTarget.getUserRotation());
assertEquals(0, Settings.System.getInt(mMockResolver,
Settings.System.ACCELEROMETER_ROTATION));
assertEquals(Surface.ROTATION_180, Settings.System.getInt(mMockResolver,
Settings.System.USER_ROTATION));
}
@Test
public void testPersistsUserRotation_LockRotation_NonDefaultDisplay() throws Exception {
mBuilder.setIsDefaultDisplay(false).build();
freezeRotation(Surface.ROTATION_180);
assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode());
assertEquals(Surface.ROTATION_180, mTarget.getUserRotation());
verify(mMockDisplayWindowSettings).setUserRotation(mMockDisplayContent,
WindowManagerPolicy.USER_ROTATION_LOCKED, Surface.ROTATION_180);
}
@Test
public void testPersistUserRotation_UnlockRotation_DefaultDisplay() throws Exception {
mBuilder.build();
thawRotation();
assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode());
assertEquals(1, Settings.System.getInt(mMockResolver,
Settings.System.ACCELEROMETER_ROTATION));
}
@Test
public void testPersistsUserRotation_UnlockRotation_NonDefaultDisplay() throws Exception {
mBuilder.setIsDefaultDisplay(false).build();
thawRotation();
assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode());
verify(mMockDisplayWindowSettings).setUserRotation(same(mMockDisplayContent),
eq(WindowManagerPolicy.USER_ROTATION_FREE), anyInt());
}
@Test
public void testPersistsFixedToUserRotation() throws Exception {
mBuilder.build();
mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent,
FIXED_TO_USER_ROTATION_ENABLED);
reset(mMockDisplayWindowSettings);
mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_DISABLED);
verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent,
FIXED_TO_USER_ROTATION_DISABLED);
reset(mMockDisplayWindowSettings);
mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_DEFAULT);
verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent,
FIXED_TO_USER_ROTATION_DEFAULT);
}
// ========================================
// Tests for User Rotation based Rotation
// ========================================
@Test
public void testReturnsUserRotation_UserRotationLocked_NoAppRequest()
throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
freezeRotation(Surface.ROTATION_180);
assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
}
@Test
public void testReturnsLandscape_UserRotationLockedSeascape_AppRequestsLandscape()
throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */,
false /* isTv */);
freezeRotation(Surface.ROTATION_180);
assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90));
}
@Test
public void testReturnsSeascape_UserRotationLockedSeascape_AppRequestsSeascape()
throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */,
false /* isTv */);
freezeRotation(Surface.ROTATION_180);
assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE, Surface.ROTATION_90));
}
@Test
public void testReturnsPortrait_UserRotationLockedPortrait_AppRequestsPortrait()
throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */,
false /* isTv */);
freezeRotation(Surface.ROTATION_270);
assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_0));
}
@Test
public void testReturnsUpsideDown_UserRotationLockedUpsideDown_AppRequestsUpsideDown()
throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */,
false /* isTv */);
freezeRotation(Surface.ROTATION_90);
assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT, Surface.ROTATION_0));
}
@Test
public void testReturnsSideways_UserRotationLocked_IncompatibleAppRequest()
throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
freezeRotation(Surface.ROTATION_180);
final int rotation = mTarget.rotationForOrientation(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_90);
assertTrue("Rotation should be sideways, but it's "
+ Surface.rotationToString(rotation),
rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
}
// =================================
// Tests for Sensor based Rotation
// =================================
private void verifyOrientationListenerRegistration(int numOfInvocation) {
final ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(
SensorEventListener.class);
waitForUiHandler();
verify(mMockSensorManager, times(numOfInvocation)).registerListener(
listenerCaptor.capture(),
same(mFakeOrientationSensor),
anyInt(),
any());
if (numOfInvocation > 0) {
mOrientationSensorListener = listenerCaptor.getValue();
}
}
@Test
public void testNotEnablesSensor_AutoRotationNotSupported() throws Exception {
mBuilder.setSupportAutoRotation(false).build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
thawRotation();
when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
when(mMockDisplayPolicy.isAwake()).thenReturn(true);
when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
mTarget.updateOrientationListener();
verifyOrientationListenerRegistration(0);
}
@Test
public void testNotEnablesSensor_ScreenNotOn() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
thawRotation();
when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(false);
when(mMockDisplayPolicy.isAwake()).thenReturn(true);
when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
mTarget.updateOrientationListener();
verifyOrientationListenerRegistration(0);
}
@Test
public void testNotEnablesSensor_NotAwake() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
thawRotation();
when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
when(mMockDisplayPolicy.isAwake()).thenReturn(false);
when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
mTarget.updateOrientationListener();
verifyOrientationListenerRegistration(0);
}
@Test
public void testNotEnablesSensor_KeyguardNotDrawnCompletely() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
thawRotation();
when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
when(mMockDisplayPolicy.isAwake()).thenReturn(true);
when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(false);
when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
mTarget.updateOrientationListener();
verifyOrientationListenerRegistration(0);
}
@Test
public void testNotEnablesSensor_WindowManagerNotDrawnCompletely() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
thawRotation();
when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
when(mMockDisplayPolicy.isAwake()).thenReturn(true);
when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(false);
mTarget.updateOrientationListener();
verifyOrientationListenerRegistration(0);
}
@Test
public void testNotEnablesSensor_FixedUserRotation() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
when(mMockDisplayPolicy.isAwake()).thenReturn(true);
when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
mTarget.updateOrientationListener();
verifyOrientationListenerRegistration(0);
}
@Test
public void testNotEnablesSensor_ForceDefaultRotation_Car() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
when(mMockDisplayPolicy.isAwake()).thenReturn(true);
when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
mTarget.updateOrientationListener();
verifyOrientationListenerRegistration(0);
}
@Test
public void testNotEnablesSensor_ForceDefaultRotation_Tv() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
when(mMockDisplayPolicy.isAwake()).thenReturn(true);
when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
mTarget.updateOrientationListener();
verifyOrientationListenerRegistration(0);
}
private void enableOrientationSensor() {
when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
when(mMockDisplayPolicy.isAwake()).thenReturn(true);
when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
mTarget.updateOrientationListener();
verifyOrientationListenerRegistration(1);
}
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 = sensor;
event.values[0] = value;
event.timestamp = SystemClock.elapsedRealtimeNanos();
return event;
}
@Test
public void testReturnsSensorRotation_RotationThawed() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
thawRotation();
enableOrientationSensor();
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
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);
try {
return latch.await(UI_HANDLER_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignored) {
}
throw new AssertionError("Failed to wait for ui handler");
}
@Test
public void testUpdatesRotationWhenSensorUpdates_RotationThawed() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
thawRotation();
enableOrientationSensor();
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
assertTrue(waitForUiHandler());
verify(sMockWm).updateRotation(false, false);
}
@Test
public void testNotifiesChoiceWhenSensorUpdates_RotationLocked() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
freezeRotation(Surface.ROTATION_270);
enableOrientationSensor();
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
assertTrue(waitForUiHandler());
verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true);
}
@Test
public void testAllowAllRotations_allowsUpsideDownSuggestion()
throws Exception {
mBuilder.build();
mTarget.updateOrientation(SCREEN_ORIENTATION_UNSPECIFIED, true);
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
.thenReturn(true);
freezeRotation(Surface.ROTATION_0);
enableOrientationSensor();
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
assertTrue(waitForUiHandler());
verify(mMockStatusBarManagerInternal)
.onProposedRotationChanged(Surface.ROTATION_180, true);
}
@Test
public void testDoNotAllowAllRotations_doesNotAllowUpsideDownSuggestion()
throws Exception {
mBuilder.build();
mTarget.updateOrientation(SCREEN_ORIENTATION_UNSPECIFIED, true);
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
.thenReturn(false);
freezeRotation(Surface.ROTATION_0);
enableOrientationSensor();
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
assertTrue(waitForUiHandler());
verify(mMockStatusBarManagerInternal)
.onProposedRotationChanged(Surface.ROTATION_180, false);
}
@Test
public void testAllowAllRotations_allowAllRotationsBecomesDisabled_forbidsUpsideDownSuggestion()
throws Exception {
mBuilder.build();
mTarget.updateOrientation(SCREEN_ORIENTATION_UNSPECIFIED, true);
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
.thenReturn(true);
freezeRotation(Surface.ROTATION_0);
enableOrientationSensor();
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
assertTrue(waitForUiHandler());
// Change resource to disallow all rotations.
// Reset "allowAllRotations".
mTarget.applyCurrentRotation(Surface.ROTATION_0);
clearInvocations(mMockStatusBarManagerInternal);
when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
.thenReturn(false);
mTarget.resetAllowAllRotations();
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
assertTrue(waitForUiHandler());
verify(mMockStatusBarManagerInternal)
.onProposedRotationChanged(Surface.ROTATION_180, false);
}
@Test
public void testReturnsCompatibleRotation_SensorEnabled_RotationThawed() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
thawRotation();
enableOrientationSensor();
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
final int rotation = mTarget.rotationForOrientation(SCREEN_ORIENTATION_LANDSCAPE,
Surface.ROTATION_0);
assertTrue("Rotation should be sideways but it's "
+ Surface.rotationToString(rotation),
rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
}
@Test
public void testReturnsUserRotation_SensorEnabled_RotationLocked() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
freezeRotation(Surface.ROTATION_270);
enableOrientationSensor();
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
}
@Test
public void testReturnsSensorRotation_180degrees_allRotationsAllowed()
throws Exception {
mBuilder.build();
when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
.thenReturn(true);
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
enableOrientationSensor();
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0));
}
@Test
public void testReturnLastRotation_sensor180_allRotationsNotAllowed()
throws Exception {
mBuilder.build();
when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
.thenReturn(false);
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
enableOrientationSensor();
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0));
}
@Test
public void testAllowRotationsIsCached()
throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
enableOrientationSensor();
// Rotate once to read the resource
when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
.thenReturn(true);
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
mTarget.rotationForOrientation(SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0);
// Change resource to disallow all rotations.
// Rotate again and 180 degrees rotation should still be returned even if "disallowed".
when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
.thenReturn(false);
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0));
}
@Test
public void testResetAllowRotations()
throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
enableOrientationSensor();
// Rotate once to read the resource
when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
.thenReturn(true);
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
mTarget.rotationForOrientation(SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0);
// Change resource to disallow all rotations.
// Reset "allowAllRotations".
// Rotate again and 180 degrees rotation should not be allowed anymore.
when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
.thenReturn(false);
mTarget.resetAllowAllRotations();
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0));
}
@Test
public void testProposedRotationListener() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
enableOrientationSensor();
final int[] receivedRotation = new int[1];
final IBinder listenerBinder = mock(IBinder.class);
final IRotationWatcher listener = new IRotationWatcher() {
@Override
public IBinder asBinder() {
return listenerBinder;
}
@Override
public void onRotationChanged(int rotation) {
receivedRotation[0] = rotation;
}
};
final RotationWatcherController controller = new RotationWatcherController(sMockWm) {
final WindowContainer<?> mContainer = mock(WindowContainer.class);
@Override
WindowContainer<?> getAssociatedWindowContainer(IBinder token) {
return mContainer;
}
};
mTarget.mProposedRotationCallback =
rotation -> controller.dispatchProposedRotation(null /* display */, rotation);
controller.registerProposedRotationListener(listener, mock(IBinder.class));
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
assertEquals(Surface.ROTATION_90, receivedRotation[0]);
controller.removeRotationWatcher(listener);
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
assertEquals(Surface.ROTATION_90, receivedRotation[0]);
}
// ====================================================
// Tests for half-fold auto-rotate override of rotation
// ====================================================
@Test
public void testUpdatesRotationWhenSensorUpdates_RotationLocked_HalfFolded() throws Exception {
mBuilder.setSupportHalfFoldAutoRotateOverride(true);
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
enableOrientationSensor();
mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
freezeRotation(Surface.ROTATION_270);
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
assertTrue(waitForUiHandler());
// No rotation...
assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
// ... until half-fold
mTarget.foldStateChanged(DeviceStateController.DeviceState.HALF_FOLDED);
assertTrue(waitForUiHandler());
verify(sMockWm).updateRotation(false, false);
assertTrue(waitForUiHandler());
assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
// ... then transition back to flat
mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
assertTrue(waitForUiHandler());
verify(sMockWm, atLeast(1)).updateRotation(false, false);
assertTrue(waitForUiHandler());
assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
}
// =================================
// Tests for Policy based Rotation
// =================================
@Test
public void testReturnsUserRotation_ForceDefaultRotation_Car() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
Surface.ROTATION_180));
}
@Test
public void testReturnsUserRotation_ForceDefaultRotation_Tv() throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
Surface.ROTATION_180));
}
@Test
public void testReturnsLidOpenRotation_LidOpen() throws Exception {
mBuilder.setLidOpenRotation(Surface.ROTATION_90).build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
when(mMockDisplayPolicy.getLidState()).thenReturn(
WindowManagerPolicy.WindowManagerFuncs.LID_OPEN);
freezeRotation(Surface.ROTATION_270);
assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
}
@Test
public void testReturnsCarDockRotation_CarDockedMode() throws Exception {
mBuilder.setCarDockRotation(Surface.ROTATION_270).build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_CAR);
freezeRotation(Surface.ROTATION_90);
assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
}
@Test
public void testReturnsDeskDockRotation_DeskDockedMode() throws Exception {
mBuilder.setDeskDockRotation(Surface.ROTATION_270).build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_DESK);
freezeRotation(Surface.ROTATION_90);
assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
}
@Test
public void testIgnoresDeskDockRotation_whenNoSensorAndLockedRespected() throws Exception {
mBuilder.setDeskDockRotation(Surface.ROTATION_270).build();
when(mMockDisplayPolicy.isDeskDockRespectsNoSensorAndLockedWithoutAccelerometer())
.thenReturn(true);
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_DESK);
freezeRotation(Surface.ROTATION_90);
assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_LOCKED, Surface.ROTATION_90));
assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_NOSENSOR, Surface.ROTATION_90));
}
@Test
public void testReturnsUserRotation_FixedToUserRotation_IgnoreIncompatibleAppRequest()
throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
freezeRotation(Surface.ROTATION_180);
final int rotation = mTarget.rotationForOrientation(
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90);
assertEquals(Surface.ROTATION_180, rotation);
}
@Test
public void testReturnsUserRotation_NonDefaultDisplay() throws Exception {
mBuilder.setIsDefaultDisplay(false).build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
freezeRotation(Surface.ROTATION_90);
assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
}
@Test
public void testShouldRotateSeamlessly() throws Exception {
mBuilder.build();
final WindowState win = mock(WindowState.class);
win.mToken = win.mActivityRecord = mock(ActivityRecord.class);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
attrs.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
doReturn(attrs).when(win).getAttrs();
doReturn(true).when(mMockDisplayPolicy).navigationBarCanMove();
doReturn(win).when(mMockDisplayPolicy).getTopFullscreenOpaqueWindow();
mMockDisplayContent.mCurrentFocus = win;
// This should not affect the condition of shouldRotateSeamlessly.
mTarget.mUpsideDownRotation = Surface.ROTATION_90;
doReturn(true).when(win.mActivityRecord).matchParentBounds();
// The focused fullscreen opaque window without override bounds should be able to be
// rotated seamlessly.
assertTrue(mTarget.shouldRotateSeamlessly(
Surface.ROTATION_0, Surface.ROTATION_90, false /* forceUpdate */));
// Reject any 180 degree because non-movable navbar will be placed in a different position.
doReturn(false).when(mMockDisplayPolicy).navigationBarCanMove();
assertFalse(mTarget.shouldRotateSeamlessly(
Surface.ROTATION_90, Surface.ROTATION_180, false /* forceUpdate */));
doReturn(true).when(mMockDisplayPolicy).navigationBarCanMove();
doReturn(false).when(win.mActivityRecord).matchParentBounds();
// No seamless rotation if the window may be positioned with offset after rotation.
assertFalse(mTarget.shouldRotateSeamlessly(
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
// ========================
@Test
public void testIsNotFixedToUserRotationByDefault() throws Exception {
mBuilder.build();
assertFalse("Display rotation should respect app requested orientation by"
+ " default.", mTarget.isFixedToUserRotation());
}
@Test
public void testIsFixedToUserRotation() throws Exception {
mBuilder.build();
mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
assertTrue("Display rotation shouldn't respect app requested orientation if"
+ " 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.
*/
private void configureDisplayRotation(int displayOrientation, boolean isCar, boolean isTv) {
final int width;
final int height;
switch (displayOrientation) {
case SCREEN_ORIENTATION_LANDSCAPE:
width = 1920;
height = 1080;
break;
case SCREEN_ORIENTATION_PORTRAIT:
width = 1080;
height = 1920;
break;
default:
throw new IllegalArgumentException("displayOrientation needs to be either landscape"
+ " or portrait, but we got "
+ ActivityInfo.screenOrientationToString(displayOrientation));
}
final PackageManager mockPackageManager = mock(PackageManager.class);
when(mMockContext.getPackageManager()).thenReturn(mockPackageManager);
when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
.thenReturn(isCar);
when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
.thenReturn(isTv);
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);
if (mTarget.isDefaultDisplay) {
mAccelerometerRotationObserver.onChange(false);
mUserRotationObserver.onChange(false);
}
}
private void thawRotation() {
mTarget.thawRotation();
if (mTarget.isDefaultDisplay) {
mAccelerometerRotationObserver.onChange(false);
mUserRotationObserver.onChange(false);
}
}
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;
private int mDeskDockRotation;
private int mUndockedHdmiRotation;
private DisplayRotationBuilder setIsDefaultDisplay(boolean isDefaultDisplay) {
mIsDefaultDisplay = isDefaultDisplay;
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;
}
private DisplayRotationBuilder setLidOpenRotation(int rotation) {
mLidOpenRotation = rotation;
return this;
}
private DisplayRotationBuilder setCarDockRotation(int rotation) {
mCarDockRotation = rotation;
return this;
}
private DisplayRotationBuilder setDeskDockRotation(int rotation) {
mDeskDockRotation = rotation;
return this;
}
private DisplayRotationBuilder setUndockedHdmiRotation(int rotation) {
mUndockedHdmiRotation = rotation;
return this;
}
private DisplayRotationBuilder setSupportHalfFoldAutoRotateOverride(
boolean supportHalfFoldAutoRotateOverride) {
mSupportHalfFoldAutoRotateOverride = supportHalfFoldAutoRotateOverride;
return this;
}
private void captureObservers() {
ArgumentCaptor<ContentObserver> captor = ArgumentCaptor.forClass(
ContentObserver.class);
verify(mMockResolver, atMost(1)).registerContentObserver(
eq(Settings.Secure.getUriFor(Settings.Secure.SHOW_ROTATION_SUGGESTIONS)),
anyBoolean(),
captor.capture(),
anyInt());
if (!captor.getAllValues().isEmpty()) {
mShowRotationSuggestionsObserver = captor.getValue();
}
captor = ArgumentCaptor.forClass(ContentObserver.class);
verify(mMockResolver, atMost(1)).registerContentObserver(
eq(Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION)),
anyBoolean(),
captor.capture(),
anyInt());
if (!captor.getAllValues().isEmpty()) {
mAccelerometerRotationObserver = captor.getValue();
}
captor = ArgumentCaptor.forClass(ContentObserver.class);
verify(mMockResolver, atMost(1)).registerContentObserver(
eq(Settings.System.getUriFor(Settings.System.USER_ROTATION)),
anyBoolean(),
captor.capture(),
anyInt());
if (!captor.getAllValues().isEmpty()) {
mUserRotationObserver = captor.getValue();
}
}
private Sensor createSensor(int type) throws Exception {
Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
constr.setAccessible(true);
Sensor sensor = constr.newInstance();
setSensorType(sensor, type);
setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type);
setSensorField(sensor, "mVendor", "Mock Vendor");
setSensorField(sensor, "mVersion", 1);
setSensorField(sensor, "mHandle", -1);
setSensorField(sensor, "mMaxRange", 10);
setSensorField(sensor, "mResolution", 1);
setSensorField(sensor, "mPower", 1);
setSensorField(sensor, "mMinDelay", 1000);
setSensorField(sensor, "mMaxDelay", 1000000000);
setSensorField(sensor, "mFlags", 0);
setSensorField(sensor, "mId", -1);
return sensor;
}
private void setSensorType(Sensor sensor, int type) throws Exception {
Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
setter.setAccessible(true);
setter.invoke(sensor, type);
}
private void setSensorField(Sensor sensor, String fieldName, Object value)
throws Exception {
Field field = Sensor.class.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(sensor, value);
}
private int convertRotationToDegrees(@Surface.Rotation int rotation) {
switch (rotation) {
case Surface.ROTATION_0:
return 0;
case Surface.ROTATION_90:
return 90;
case Surface.ROTATION_180:
return 180;
case Surface.ROTATION_270:
return 270;
default:
return -1;
}
}
private void build() throws Exception {
mMockContext = mock(Context.class);
mMockDisplayContent = mock(DisplayContent.class);
mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay;
when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt()))
.thenReturn(NO_CUTOUT);
when(mMockDisplayContent.getDefaultTaskDisplayArea())
.thenReturn(mock(TaskDisplayArea.class));
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))
.thenReturn(convertRotationToDegrees(mLidOpenRotation));
when(mMockRes.getInteger(com.android.internal.R.integer.config_carDockRotation))
.thenReturn(convertRotationToDegrees(mCarDockRotation));
when(mMockRes.getInteger(com.android.internal.R.integer.config_deskDockRotation))
.thenReturn(convertRotationToDegrees(mDeskDockRotation));
when(mMockRes.getInteger(com.android.internal.R.integer.config_undockedHdmiRotation))
.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))
.thenReturn(mSupportHalfFoldAutoRotateOverride);
mMockResolver = mock(ContentResolver.class);
when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
mFakeSettingsProvider = new FakeSettingsProvider();
when(mMockResolver.acquireProvider(Settings.AUTHORITY))
.thenReturn(mFakeSettingsProvider.getIContentProvider());
mMockDisplayAddress = mock(DisplayAddress.class);
mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
mMockDeviceStateManager = mock(DeviceStateManager.class);
when(mMockContext.getSystemService(eq(DeviceStateManager.class)))
.thenReturn(mMockDeviceStateManager);
mDeviceStateController = mock(DeviceStateController.class);
mTarget = new TestDisplayRotation(mMockDisplayContent, mMockDisplayAddress,
mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext,
mDeviceStateController);
reset(sMockWm);
verify(mMockSensorManager, atLeast(0)).registerListener(
mHingeAngleSensorListenerCaptor.capture(), eq(mFakeHingeAngleSensor), anyInt(),
any());
captureObservers();
}
}
private static class TestDisplayRotation extends DisplayRotation {
IntConsumer mProposedRotationCallback;
TestDisplayRotation(DisplayContent dc, DisplayAddress address, DisplayPolicy policy,
DisplayWindowSettings displayWindowSettings, Context context,
DeviceStateController deviceStateController) {
super(sMockWm, dc, address, policy, displayWindowSettings, context, new Object(),
deviceStateController, mock(DisplayRotationCoordinator.class));
}
@Override
DisplayRotationImmersiveAppCompatPolicy initImmersiveAppCompatPolicy(
WindowManagerService service, DisplayContent displayContent) {
return null;
}
@Override
void dispatchProposedRotation(int rotation) {
if (mProposedRotationCallback != null) {
mProposedRotationCallback.accept(rotation);
}
}
@Override
Handler getHandler() {
return sHandler;
}
@Override
long uptimeMillis() {
return sCurrentUptimeMillis;
}
}
}