1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 20 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 21 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 22 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT; 23 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DISABLED; 24 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED; 25 26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; 27 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; 28 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; 29 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atMost; 30 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; 32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; 34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.same; 35 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; 36 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 37 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 38 39 import static org.junit.Assert.assertEquals; 40 import static org.junit.Assert.assertFalse; 41 import static org.junit.Assert.assertTrue; 42 43 import android.content.ContentResolver; 44 import android.content.Context; 45 import android.content.Intent; 46 import android.content.pm.ActivityInfo; 47 import android.content.pm.PackageManager; 48 import android.content.res.Resources; 49 import android.database.ContentObserver; 50 import android.hardware.Sensor; 51 import android.hardware.SensorEvent; 52 import android.hardware.SensorEventListener; 53 import android.hardware.SensorManager; 54 import android.os.PowerManagerInternal; 55 import android.os.SystemClock; 56 import android.platform.test.annotations.Presubmit; 57 import android.provider.Settings; 58 import android.view.Surface; 59 import android.view.WindowManager; 60 61 import androidx.test.filters.SmallTest; 62 63 import com.android.internal.util.test.FakeSettingsProvider; 64 import com.android.server.LocalServices; 65 import com.android.server.UiThread; 66 import com.android.server.policy.WindowManagerPolicy; 67 import com.android.server.statusbar.StatusBarManagerInternal; 68 import com.android.server.wm.utils.WmDisplayCutout; 69 70 import org.junit.After; 71 import org.junit.AfterClass; 72 import org.junit.Before; 73 import org.junit.BeforeClass; 74 import org.junit.Test; 75 import org.mockito.ArgumentCaptor; 76 77 import java.lang.reflect.Constructor; 78 import java.lang.reflect.Field; 79 import java.lang.reflect.Method; 80 import java.util.Collections; 81 import java.util.concurrent.CountDownLatch; 82 import java.util.concurrent.TimeUnit; 83 84 /** 85 * Test class for {@link DisplayRotation}. 86 * 87 * Build/Install/Run: 88 * atest WmTests:DisplayRotationTests 89 */ 90 @SmallTest 91 @Presubmit 92 public class DisplayRotationTests { 93 private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50; 94 95 private StatusBarManagerInternal mPreviousStatusBarManagerInternal; 96 97 private static WindowManagerService sMockWm; 98 private DisplayContent mMockDisplayContent; 99 private DisplayPolicy mMockDisplayPolicy; 100 private Context mMockContext; 101 private Resources mMockRes; 102 private SensorManager mMockSensorManager; 103 private Sensor mFakeSensor; 104 private DisplayWindowSettings mMockDisplayWindowSettings; 105 private ContentResolver mMockResolver; 106 private FakeSettingsProvider mFakeSettingsProvider; 107 private StatusBarManagerInternal mMockStatusBarManagerInternal; 108 109 // Fields below are callbacks captured from test target. 110 private ContentObserver mShowRotationSuggestionsObserver; 111 private ContentObserver mAccelerometerRotationObserver; 112 private ContentObserver mUserRotationObserver; 113 private SensorEventListener mOrientationSensorListener; 114 115 private DisplayRotationBuilder mBuilder; 116 117 private DisplayRotation mTarget; 118 119 @BeforeClass setUpOnce()120 public static void setUpOnce() { 121 sMockWm = mock(WindowManagerService.class); 122 sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class); 123 sMockWm.mPolicy = mock(WindowManagerPolicy.class); 124 } 125 126 @AfterClass tearDownOnce()127 public static void tearDownOnce() { 128 // Make sure the fake settings are cleared after the last test method. 129 FakeSettingsProvider.clearSettingsProvider(); 130 } 131 132 @Before setUp()133 public void setUp() { 134 FakeSettingsProvider.clearSettingsProvider(); 135 136 mPreviousStatusBarManagerInternal = LocalServices.getService( 137 StatusBarManagerInternal.class); 138 LocalServices.removeServiceForTest(StatusBarManagerInternal.class); 139 mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class); 140 LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal); 141 142 mBuilder = new DisplayRotationBuilder(); 143 } 144 145 @After tearDown()146 public void tearDown() { 147 LocalServices.removeServiceForTest(StatusBarManagerInternal.class); 148 if (mPreviousStatusBarManagerInternal != null) { 149 LocalServices.addService(StatusBarManagerInternal.class, 150 mPreviousStatusBarManagerInternal); 151 mPreviousStatusBarManagerInternal = null; 152 } 153 } 154 155 // ================================ 156 // Display Settings Related Tests 157 // ================================ 158 @Test testLocksUserRotation_LockRotation_DefaultDisplay()159 public void testLocksUserRotation_LockRotation_DefaultDisplay() throws Exception { 160 mBuilder.build(); 161 162 freezeRotation(Surface.ROTATION_180); 163 164 assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode()); 165 assertEquals(Surface.ROTATION_180, mTarget.getUserRotation()); 166 167 assertEquals(0, Settings.System.getInt(mMockResolver, 168 Settings.System.ACCELEROMETER_ROTATION)); 169 assertEquals(Surface.ROTATION_180, Settings.System.getInt(mMockResolver, 170 Settings.System.USER_ROTATION)); 171 } 172 173 @Test testPersistsUserRotation_LockRotation_NonDefaultDisplay()174 public void testPersistsUserRotation_LockRotation_NonDefaultDisplay() throws Exception { 175 mBuilder.setIsDefaultDisplay(false).build(); 176 177 freezeRotation(Surface.ROTATION_180); 178 179 assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode()); 180 assertEquals(Surface.ROTATION_180, mTarget.getUserRotation()); 181 182 verify(mMockDisplayWindowSettings).setUserRotation(mMockDisplayContent, 183 WindowManagerPolicy.USER_ROTATION_LOCKED, Surface.ROTATION_180); 184 } 185 186 @Test testPersistUserRotation_UnlockRotation_DefaultDisplay()187 public void testPersistUserRotation_UnlockRotation_DefaultDisplay() throws Exception { 188 mBuilder.build(); 189 190 thawRotation(); 191 192 assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode()); 193 194 assertEquals(1, Settings.System.getInt(mMockResolver, 195 Settings.System.ACCELEROMETER_ROTATION)); 196 } 197 198 @Test testPersistsUserRotation_UnlockRotation_NonDefaultDisplay()199 public void testPersistsUserRotation_UnlockRotation_NonDefaultDisplay() throws Exception { 200 mBuilder.setIsDefaultDisplay(false).build(); 201 202 thawRotation(); 203 204 assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode()); 205 206 verify(mMockDisplayWindowSettings).setUserRotation(same(mMockDisplayContent), 207 eq(WindowManagerPolicy.USER_ROTATION_FREE), anyInt()); 208 } 209 210 @Test testPersistsFixedToUserRotation()211 public void testPersistsFixedToUserRotation() throws Exception { 212 mBuilder.build(); 213 214 mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); 215 216 verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, 217 FIXED_TO_USER_ROTATION_ENABLED); 218 219 reset(mMockDisplayWindowSettings); 220 mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_DISABLED); 221 222 verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, 223 FIXED_TO_USER_ROTATION_DISABLED); 224 225 reset(mMockDisplayWindowSettings); 226 mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_DEFAULT); 227 228 verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, 229 FIXED_TO_USER_ROTATION_DEFAULT); 230 } 231 232 // ======================================== 233 // Tests for User Rotation based Rotation 234 // ======================================== 235 @Test testReturnsUserRotation_UserRotationLocked_NoAppRequest()236 public void testReturnsUserRotation_UserRotationLocked_NoAppRequest() 237 throws Exception { 238 mBuilder.build(); 239 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); 240 241 freezeRotation(Surface.ROTATION_180); 242 243 assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation( 244 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90)); 245 } 246 247 @Test testReturnsLandscape_UserRotationLockedSeascape_AppRequestsLandscape()248 public void testReturnsLandscape_UserRotationLockedSeascape_AppRequestsLandscape() 249 throws Exception { 250 mBuilder.build(); 251 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */, 252 false /* isTv */); 253 254 freezeRotation(Surface.ROTATION_180); 255 256 assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation( 257 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90)); 258 } 259 260 @Test testReturnsSeascape_UserRotationLockedSeascape_AppRequestsSeascape()261 public void testReturnsSeascape_UserRotationLockedSeascape_AppRequestsSeascape() 262 throws Exception { 263 mBuilder.build(); 264 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */, 265 false /* isTv */); 266 267 freezeRotation(Surface.ROTATION_180); 268 269 assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation( 270 ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE, Surface.ROTATION_90)); 271 } 272 273 @Test testReturnsPortrait_UserRotationLockedPortrait_AppRequestsPortrait()274 public void testReturnsPortrait_UserRotationLockedPortrait_AppRequestsPortrait() 275 throws Exception { 276 mBuilder.build(); 277 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */, 278 false /* isTv */); 279 280 freezeRotation(Surface.ROTATION_270); 281 282 assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation( 283 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_0)); 284 } 285 286 @Test testReturnsUpsideDown_UserRotationLockedUpsideDown_AppRequestsUpsideDown()287 public void testReturnsUpsideDown_UserRotationLockedUpsideDown_AppRequestsUpsideDown() 288 throws Exception { 289 mBuilder.build(); 290 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */, 291 false /* isTv */); 292 293 freezeRotation(Surface.ROTATION_90); 294 295 assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation( 296 ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT, Surface.ROTATION_0)); 297 } 298 299 @Test testReturnsSideways_UserRotationLocked_IncompatibleAppRequest()300 public void testReturnsSideways_UserRotationLocked_IncompatibleAppRequest() 301 throws Exception { 302 mBuilder.build(); 303 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); 304 305 freezeRotation(Surface.ROTATION_180); 306 307 final int rotation = mTarget.rotationForOrientation( 308 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_90); 309 assertTrue("Rotation should be sideways, but it's " 310 + Surface.rotationToString(rotation), 311 rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270); 312 } 313 314 // ================================= 315 // Tests for Sensor based Rotation 316 // ================================= verifyOrientationListenerRegistration(int numOfInvocation)317 private void verifyOrientationListenerRegistration(int numOfInvocation) { 318 final ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass( 319 SensorEventListener.class); 320 verify(mMockSensorManager, times(numOfInvocation)).registerListener( 321 listenerCaptor.capture(), 322 same(mFakeSensor), 323 anyInt(), 324 any()); 325 if (numOfInvocation > 0) { 326 mOrientationSensorListener = listenerCaptor.getValue(); 327 } 328 } 329 330 @Test testNotEnablesSensor_AutoRotationNotSupported()331 public void testNotEnablesSensor_AutoRotationNotSupported() throws Exception { 332 mBuilder.setSupportAutoRotation(false).build(); 333 configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); 334 335 thawRotation(); 336 337 when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); 338 when(mMockDisplayPolicy.isAwake()).thenReturn(true); 339 when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); 340 when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); 341 mTarget.updateOrientationListener(); 342 verifyOrientationListenerRegistration(0); 343 } 344 345 @Test testNotEnablesSensor_ScreenNotOn()346 public void testNotEnablesSensor_ScreenNotOn() throws Exception { 347 mBuilder.build(); 348 configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); 349 350 thawRotation(); 351 352 when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(false); 353 when(mMockDisplayPolicy.isAwake()).thenReturn(true); 354 when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); 355 when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); 356 mTarget.updateOrientationListener(); 357 verifyOrientationListenerRegistration(0); 358 } 359 360 @Test testNotEnablesSensor_NotAwake()361 public void testNotEnablesSensor_NotAwake() throws Exception { 362 mBuilder.build(); 363 configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); 364 365 thawRotation(); 366 367 when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); 368 when(mMockDisplayPolicy.isAwake()).thenReturn(false); 369 when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); 370 when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); 371 mTarget.updateOrientationListener(); 372 verifyOrientationListenerRegistration(0); 373 } 374 375 @Test testNotEnablesSensor_KeyguardNotDrawnCompletely()376 public void testNotEnablesSensor_KeyguardNotDrawnCompletely() throws Exception { 377 mBuilder.build(); 378 configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); 379 380 thawRotation(); 381 382 when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); 383 when(mMockDisplayPolicy.isAwake()).thenReturn(true); 384 when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(false); 385 when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); 386 mTarget.updateOrientationListener(); 387 verifyOrientationListenerRegistration(0); 388 } 389 390 @Test testNotEnablesSensor_WindowManagerNotDrawnCompletely()391 public void testNotEnablesSensor_WindowManagerNotDrawnCompletely() throws Exception { 392 mBuilder.build(); 393 configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); 394 395 thawRotation(); 396 397 when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); 398 when(mMockDisplayPolicy.isAwake()).thenReturn(true); 399 when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); 400 when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(false); 401 mTarget.updateOrientationListener(); 402 verifyOrientationListenerRegistration(0); 403 } 404 405 @Test testNotEnablesSensor_FixedUserRotation()406 public void testNotEnablesSensor_FixedUserRotation() throws Exception { 407 mBuilder.build(); 408 configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); 409 410 when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); 411 when(mMockDisplayPolicy.isAwake()).thenReturn(true); 412 when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); 413 when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); 414 mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); 415 mTarget.updateOrientationListener(); 416 verifyOrientationListenerRegistration(0); 417 } 418 419 @Test testNotEnablesSensor_ForceDefaultRotation_Car()420 public void testNotEnablesSensor_ForceDefaultRotation_Car() throws Exception { 421 mBuilder.build(); 422 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false); 423 424 when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); 425 when(mMockDisplayPolicy.isAwake()).thenReturn(true); 426 when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); 427 when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); 428 mTarget.updateOrientationListener(); 429 verifyOrientationListenerRegistration(0); 430 } 431 432 @Test testNotEnablesSensor_ForceDefaultRotation_Tv()433 public void testNotEnablesSensor_ForceDefaultRotation_Tv() throws Exception { 434 mBuilder.build(); 435 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true); 436 437 when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); 438 when(mMockDisplayPolicy.isAwake()).thenReturn(true); 439 when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); 440 when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); 441 mTarget.updateOrientationListener(); 442 verifyOrientationListenerRegistration(0); 443 } 444 enableOrientationSensor()445 private void enableOrientationSensor() { 446 when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); 447 when(mMockDisplayPolicy.isAwake()).thenReturn(true); 448 when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); 449 when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); 450 mTarget.updateOrientationListener(); 451 verifyOrientationListenerRegistration(1); 452 } 453 createSensorEvent(int rotation)454 private SensorEvent createSensorEvent(int rotation) throws Exception { 455 final Constructor<SensorEvent> constructor = 456 SensorEvent.class.getDeclaredConstructor(int.class); 457 constructor.setAccessible(true); 458 final SensorEvent event = constructor.newInstance(1); 459 event.sensor = mFakeSensor; 460 event.values[0] = rotation; 461 event.timestamp = SystemClock.elapsedRealtimeNanos(); 462 return event; 463 } 464 465 @Test testReturnsSensorRotation_RotationThawed()466 public void testReturnsSensorRotation_RotationThawed() throws Exception { 467 mBuilder.build(); 468 configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); 469 470 thawRotation(); 471 472 enableOrientationSensor(); 473 474 mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90)); 475 476 assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation( 477 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); 478 } 479 waitForUiHandler()480 private boolean waitForUiHandler() throws Exception { 481 final CountDownLatch latch = new CountDownLatch(1); 482 UiThread.getHandler().post(latch::countDown); 483 return latch.await(UI_HANDLER_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); 484 } 485 486 @Test testUpdatesRotationWhenSensorUpdates_RotationThawed()487 public void testUpdatesRotationWhenSensorUpdates_RotationThawed() throws Exception { 488 mBuilder.build(); 489 configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); 490 491 thawRotation(); 492 493 enableOrientationSensor(); 494 495 mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90)); 496 assertTrue(waitForUiHandler()); 497 498 verify(sMockWm).updateRotation(false, false); 499 } 500 501 @Test testNotifiesChoiceWhenSensorUpdates_RotationLocked()502 public void testNotifiesChoiceWhenSensorUpdates_RotationLocked() throws Exception { 503 mBuilder.build(); 504 configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); 505 506 freezeRotation(Surface.ROTATION_270); 507 508 enableOrientationSensor(); 509 510 mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90)); 511 assertTrue(waitForUiHandler()); 512 513 verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true); 514 } 515 516 @Test testReturnsCompatibleRotation_SensorEnabled_RotationThawed()517 public void testReturnsCompatibleRotation_SensorEnabled_RotationThawed() throws Exception { 518 mBuilder.build(); 519 configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); 520 521 thawRotation(); 522 523 enableOrientationSensor(); 524 525 mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180)); 526 527 final int rotation = mTarget.rotationForOrientation(SCREEN_ORIENTATION_LANDSCAPE, 528 Surface.ROTATION_0); 529 assertTrue("Rotation should be sideways but it's " 530 + Surface.rotationToString(rotation), 531 rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270); 532 } 533 534 @Test testReturnsUserRotation_SensorEnabled_RotationLocked()535 public void testReturnsUserRotation_SensorEnabled_RotationLocked() throws Exception { 536 mBuilder.build(); 537 configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); 538 539 freezeRotation(Surface.ROTATION_270); 540 541 enableOrientationSensor(); 542 543 mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180)); 544 545 assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation( 546 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); 547 } 548 549 // ================================= 550 // Tests for Policy based Rotation 551 // ================================= 552 @Test testReturnsUserRotation_ForceDefaultRotation_Car()553 public void testReturnsUserRotation_ForceDefaultRotation_Car() throws Exception { 554 mBuilder.build(); 555 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false); 556 557 assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT, 558 Surface.ROTATION_180)); 559 } 560 561 @Test testReturnsUserRotation_ForceDefaultRotation_Tv()562 public void testReturnsUserRotation_ForceDefaultRotation_Tv() throws Exception { 563 mBuilder.build(); 564 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true); 565 566 assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT, 567 Surface.ROTATION_180)); 568 } 569 570 @Test testReturnsLidOpenRotation_LidOpen()571 public void testReturnsLidOpenRotation_LidOpen() throws Exception { 572 mBuilder.setLidOpenRotation(Surface.ROTATION_90).build(); 573 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); 574 575 when(mMockDisplayPolicy.getLidState()).thenReturn( 576 WindowManagerPolicy.WindowManagerFuncs.LID_OPEN); 577 578 freezeRotation(Surface.ROTATION_270); 579 580 assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation( 581 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); 582 } 583 584 @Test testReturnsCarDockRotation_CarDockedMode()585 public void testReturnsCarDockRotation_CarDockedMode() throws Exception { 586 mBuilder.setCarDockRotation(Surface.ROTATION_270).build(); 587 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); 588 589 when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_CAR); 590 591 freezeRotation(Surface.ROTATION_90); 592 593 assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation( 594 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90)); 595 } 596 597 @Test testReturnsDeskDockRotation_DeskDockedMode()598 public void testReturnsDeskDockRotation_DeskDockedMode() throws Exception { 599 mBuilder.setDeskDockRotation(Surface.ROTATION_270).build(); 600 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); 601 602 when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_DESK); 603 604 freezeRotation(Surface.ROTATION_90); 605 606 assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation( 607 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90)); 608 } 609 610 @Test testReturnsUserRotation_FixedToUserRotation_IgnoreIncompatibleAppRequest()611 public void testReturnsUserRotation_FixedToUserRotation_IgnoreIncompatibleAppRequest() 612 throws Exception { 613 mBuilder.build(); 614 configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); 615 616 mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); 617 618 freezeRotation(Surface.ROTATION_180); 619 620 final int rotation = mTarget.rotationForOrientation( 621 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90); 622 assertEquals(Surface.ROTATION_180, rotation); 623 } 624 625 @Test testReturnsUserRotation_NonDefaultDisplay()626 public void testReturnsUserRotation_NonDefaultDisplay() throws Exception { 627 mBuilder.setIsDefaultDisplay(false).build(); 628 configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); 629 630 freezeRotation(Surface.ROTATION_90); 631 632 assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation( 633 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); 634 } 635 636 @Test testShouldRotateSeamlessly()637 public void testShouldRotateSeamlessly() throws Exception { 638 mBuilder.build(); 639 640 final WindowState win = mock(WindowState.class); 641 win.mToken = win.mActivityRecord = mock(ActivityRecord.class); 642 final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(); 643 attrs.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; 644 645 doReturn(attrs).when(win).getAttrs(); 646 doReturn(true).when(mMockDisplayPolicy).navigationBarCanMove(); 647 doReturn(win).when(mMockDisplayPolicy).getTopFullscreenOpaqueWindow(); 648 mMockDisplayContent.mCurrentFocus = win; 649 mTarget.mUpsideDownRotation = Surface.ROTATION_180; 650 651 doReturn(true).when(win.mActivityRecord).matchParentBounds(); 652 // The focused fullscreen opaque window without override bounds should be able to be 653 // rotated seamlessly. 654 assertTrue(mTarget.shouldRotateSeamlessly( 655 Surface.ROTATION_0, Surface.ROTATION_90, false /* forceUpdate */)); 656 657 doReturn(false).when(win.mActivityRecord).matchParentBounds(); 658 // No seamless rotation if the window may be positioned with offset after rotation. 659 assertFalse(mTarget.shouldRotateSeamlessly( 660 Surface.ROTATION_0, Surface.ROTATION_90, false /* forceUpdate */)); 661 } 662 663 // ======================== 664 // Non-rotation API Tests 665 // ======================== 666 @Test testIsNotFixedToUserRotationByDefault()667 public void testIsNotFixedToUserRotationByDefault() throws Exception { 668 mBuilder.build(); 669 670 assertFalse("Display rotation should respect app requested orientation by" 671 + " default.", mTarget.isFixedToUserRotation()); 672 } 673 674 @Test testIsFixedToUserRotation()675 public void testIsFixedToUserRotation() throws Exception { 676 mBuilder.build(); 677 mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); 678 679 assertTrue("Display rotation shouldn't respect app requested orientation if" 680 + " fixed to user rotation.", mTarget.isFixedToUserRotation()); 681 } 682 683 /** 684 * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget} 685 * according to given parameters. 686 */ configureDisplayRotation(int displayOrientation, boolean isCar, boolean isTv)687 private void configureDisplayRotation(int displayOrientation, boolean isCar, boolean isTv) { 688 final int width; 689 final int height; 690 switch (displayOrientation) { 691 case SCREEN_ORIENTATION_LANDSCAPE: 692 width = 1920; 693 height = 1080; 694 break; 695 case SCREEN_ORIENTATION_PORTRAIT: 696 width = 1080; 697 height = 1920; 698 break; 699 default: 700 throw new IllegalArgumentException("displayOrientation needs to be either landscape" 701 + " or portrait, but we got " 702 + ActivityInfo.screenOrientationToString(displayOrientation)); 703 } 704 705 final PackageManager mockPackageManager = mock(PackageManager.class); 706 when(mMockContext.getPackageManager()).thenReturn(mockPackageManager); 707 when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) 708 .thenReturn(isCar); 709 when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) 710 .thenReturn(isTv); 711 712 final int shortSizeDp = (isCar || isTv) ? 540 : 720; 713 final int longSizeDp = 960; 714 mTarget.configure(width, height, shortSizeDp, longSizeDp); 715 } 716 freezeRotation(int rotation)717 private void freezeRotation(int rotation) { 718 mTarget.freezeRotation(rotation); 719 720 if (mTarget.isDefaultDisplay) { 721 mAccelerometerRotationObserver.onChange(false); 722 mUserRotationObserver.onChange(false); 723 } 724 } 725 thawRotation()726 private void thawRotation() { 727 mTarget.thawRotation(); 728 729 if (mTarget.isDefaultDisplay) { 730 mAccelerometerRotationObserver.onChange(false); 731 mUserRotationObserver.onChange(false); 732 } 733 } 734 735 private class DisplayRotationBuilder { 736 private boolean mIsDefaultDisplay = true; 737 private boolean mSupportAutoRotation = true; 738 739 private int mLidOpenRotation = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT; 740 private int mCarDockRotation; 741 private int mDeskDockRotation; 742 private int mUndockedHdmiRotation; 743 setIsDefaultDisplay(boolean isDefaultDisplay)744 private DisplayRotationBuilder setIsDefaultDisplay(boolean isDefaultDisplay) { 745 mIsDefaultDisplay = isDefaultDisplay; 746 return this; 747 } 748 setSupportAutoRotation(boolean supportAutoRotation)749 private DisplayRotationBuilder setSupportAutoRotation(boolean supportAutoRotation) { 750 mSupportAutoRotation = supportAutoRotation; 751 return this; 752 } 753 setLidOpenRotation(int rotation)754 private DisplayRotationBuilder setLidOpenRotation(int rotation) { 755 mLidOpenRotation = rotation; 756 return this; 757 } 758 setCarDockRotation(int rotation)759 private DisplayRotationBuilder setCarDockRotation(int rotation) { 760 mCarDockRotation = rotation; 761 return this; 762 } 763 setDeskDockRotation(int rotation)764 private DisplayRotationBuilder setDeskDockRotation(int rotation) { 765 mDeskDockRotation = rotation; 766 return this; 767 } 768 setUndockedHdmiRotation(int rotation)769 private DisplayRotationBuilder setUndockedHdmiRotation(int rotation) { 770 mUndockedHdmiRotation = rotation; 771 return this; 772 } 773 captureObservers()774 private void captureObservers() { 775 ArgumentCaptor<ContentObserver> captor = ArgumentCaptor.forClass( 776 ContentObserver.class); 777 verify(mMockResolver, atMost(1)).registerContentObserver( 778 eq(Settings.Secure.getUriFor(Settings.Secure.SHOW_ROTATION_SUGGESTIONS)), 779 anyBoolean(), 780 captor.capture(), 781 anyInt()); 782 if (!captor.getAllValues().isEmpty()) { 783 mShowRotationSuggestionsObserver = captor.getValue(); 784 } 785 786 captor = ArgumentCaptor.forClass(ContentObserver.class); 787 verify(mMockResolver, atMost(1)).registerContentObserver( 788 eq(Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION)), 789 anyBoolean(), 790 captor.capture(), 791 anyInt()); 792 if (!captor.getAllValues().isEmpty()) { 793 mAccelerometerRotationObserver = captor.getValue(); 794 } 795 796 captor = ArgumentCaptor.forClass(ContentObserver.class); 797 verify(mMockResolver, atMost(1)).registerContentObserver( 798 eq(Settings.System.getUriFor(Settings.System.USER_ROTATION)), 799 anyBoolean(), 800 captor.capture(), 801 anyInt()); 802 if (!captor.getAllValues().isEmpty()) { 803 mUserRotationObserver = captor.getValue(); 804 } 805 } 806 createSensor(int type)807 private Sensor createSensor(int type) throws Exception { 808 Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor(); 809 constr.setAccessible(true); 810 Sensor sensor = constr.newInstance(); 811 812 setSensorType(sensor, type); 813 setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type); 814 setSensorField(sensor, "mVendor", "Mock Vendor"); 815 setSensorField(sensor, "mVersion", 1); 816 setSensorField(sensor, "mHandle", -1); 817 setSensorField(sensor, "mMaxRange", 10); 818 setSensorField(sensor, "mResolution", 1); 819 setSensorField(sensor, "mPower", 1); 820 setSensorField(sensor, "mMinDelay", 1000); 821 setSensorField(sensor, "mMaxDelay", 1000000000); 822 setSensorField(sensor, "mFlags", 0); 823 setSensorField(sensor, "mId", -1); 824 825 return sensor; 826 } 827 setSensorType(Sensor sensor, int type)828 private void setSensorType(Sensor sensor, int type) throws Exception { 829 Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE); 830 setter.setAccessible(true); 831 setter.invoke(sensor, type); 832 } 833 setSensorField(Sensor sensor, String fieldName, Object value)834 private void setSensorField(Sensor sensor, String fieldName, Object value) 835 throws Exception { 836 Field field = Sensor.class.getDeclaredField(fieldName); 837 field.setAccessible(true); 838 field.set(sensor, value); 839 } 840 convertRotationToDegrees(@urface.Rotation int rotation)841 private int convertRotationToDegrees(@Surface.Rotation int rotation) { 842 switch (rotation) { 843 case Surface.ROTATION_0: 844 return 0; 845 case Surface.ROTATION_90: 846 return 90; 847 case Surface.ROTATION_180: 848 return 180; 849 case Surface.ROTATION_270: 850 return 270; 851 default: 852 return -1; 853 } 854 } 855 build()856 private void build() throws Exception { 857 mMockContext = mock(Context.class); 858 859 mMockDisplayContent = mock(DisplayContent.class); 860 mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay; 861 when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt())) 862 .thenReturn(WmDisplayCutout.NO_CUTOUT); 863 when(mMockDisplayContent.getDefaultTaskDisplayArea()) 864 .thenReturn(mock(TaskDisplayArea.class)); 865 866 mMockDisplayPolicy = mock(DisplayPolicy.class); 867 868 mMockRes = mock(Resources.class); 869 when(mMockContext.getResources()).thenReturn((mMockRes)); 870 when(mMockRes.getBoolean(com.android.internal.R.bool.config_supportAutoRotation)) 871 .thenReturn(mSupportAutoRotation); 872 when(mMockRes.getInteger(com.android.internal.R.integer.config_lidOpenRotation)) 873 .thenReturn(convertRotationToDegrees(mLidOpenRotation)); 874 when(mMockRes.getInteger(com.android.internal.R.integer.config_carDockRotation)) 875 .thenReturn(convertRotationToDegrees(mCarDockRotation)); 876 when(mMockRes.getInteger(com.android.internal.R.integer.config_deskDockRotation)) 877 .thenReturn(convertRotationToDegrees(mDeskDockRotation)); 878 when(mMockRes.getInteger(com.android.internal.R.integer.config_undockedHdmiRotation)) 879 .thenReturn(convertRotationToDegrees(mUndockedHdmiRotation)); 880 881 mMockSensorManager = mock(SensorManager.class); 882 when(mMockContext.getSystemService(Context.SENSOR_SERVICE)) 883 .thenReturn(mMockSensorManager); 884 mFakeSensor = createSensor(Sensor.TYPE_DEVICE_ORIENTATION); 885 when(mMockSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION)).thenReturn( 886 Collections.singletonList(mFakeSensor)); 887 888 mMockResolver = mock(ContentResolver.class); 889 when(mMockContext.getContentResolver()).thenReturn(mMockResolver); 890 mFakeSettingsProvider = new FakeSettingsProvider(); 891 when(mMockResolver.acquireProvider(Settings.AUTHORITY)) 892 .thenReturn(mFakeSettingsProvider.getIContentProvider()); 893 894 mMockDisplayWindowSettings = mock(DisplayWindowSettings.class); 895 mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayPolicy, 896 mMockDisplayWindowSettings, mMockContext, new Object()); 897 reset(sMockWm); 898 899 captureObservers(); 900 } 901 } 902 } 903