• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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