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