• 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_LOCKED;
21 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
22 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
24 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
25 import static android.view.DisplayCutout.NO_CUTOUT;
26 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
27 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DISABLED;
28 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
29 
30 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atMost;
35 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
36 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
37 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
38 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
39 import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
40 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
41 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
43 
44 import static org.junit.Assert.assertEquals;
45 import static org.junit.Assert.assertFalse;
46 import static org.junit.Assert.assertTrue;
47 import static org.mockito.Mockito.clearInvocations;
48 
49 import android.app.WindowConfiguration;
50 import android.content.ContentResolver;
51 import android.content.Context;
52 import android.content.Intent;
53 import android.content.pm.ActivityInfo;
54 import android.content.pm.PackageManager;
55 import android.content.res.Resources;
56 import android.database.ContentObserver;
57 import android.hardware.Sensor;
58 import android.hardware.SensorEvent;
59 import android.hardware.SensorEventListener;
60 import android.hardware.SensorManager;
61 import android.hardware.devicestate.DeviceStateManager;
62 import android.os.PowerManagerInternal;
63 import android.os.SystemClock;
64 import android.os.Handler;
65 import android.platform.test.annotations.Presubmit;
66 import android.provider.Settings;
67 import android.view.DisplayAddress;
68 import android.view.Surface;
69 import android.view.WindowManager;
70 
71 import androidx.annotation.Nullable;
72 import androidx.test.filters.SmallTest;
73 
74 import com.android.internal.util.test.FakeSettingsProvider;
75 import com.android.server.LocalServices;
76 import com.android.server.UiThread;
77 import com.android.server.policy.WindowManagerPolicy;
78 import com.android.server.statusbar.StatusBarManagerInternal;
79 import com.android.server.testutils.OffsettableClock;
80 import com.android.server.testutils.TestHandler;
81 import com.android.server.wm.DisplayContent.FixedRotationTransitionListener;
82 
83 import org.junit.After;
84 import org.junit.AfterClass;
85 import org.junit.Before;
86 import org.junit.BeforeClass;
87 import org.junit.Test;
88 import org.mockito.ArgumentCaptor;
89 
90 import java.lang.reflect.Constructor;
91 import java.lang.reflect.Field;
92 import java.lang.reflect.Method;
93 import java.util.Collections;
94 import java.util.concurrent.CountDownLatch;
95 import java.util.concurrent.TimeUnit;
96 
97 /**
98  * Test class for {@link DisplayRotation}.
99  *
100  * Build/Install/Run:
101  *  atest WmTests:DisplayRotationTests
102  */
103 @SmallTest
104 @Presubmit
105 public class DisplayRotationTests {
106     private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50;
107 
108     private StatusBarManagerInternal mPreviousStatusBarManagerInternal;
109     private static final OffsettableClock sClock = new OffsettableClock.Stopped();
110     private static TestHandler sHandler;
111     private static long sCurrentUptimeMillis = 10_000;
112 
113     private static WindowManagerService sMockWm;
114     private DisplayContent mMockDisplayContent;
115     private DisplayRotationReversionController mMockDisplayRotationReversionController;
116     private DisplayPolicy mMockDisplayPolicy;
117     private DisplayAddress mMockDisplayAddress;
118     private Context mMockContext;
119     private Resources mMockRes;
120     private SensorManager mMockSensorManager;
121     private Sensor mFakeOrientationSensor;
122     private Sensor mFakeHingeAngleSensor;
123     private DisplayWindowSettings mMockDisplayWindowSettings;
124     private ContentResolver mMockResolver;
125     private FakeSettingsProvider mFakeSettingsProvider;
126     private StatusBarManagerInternal mMockStatusBarManagerInternal;
127     private DeviceStateManager mMockDeviceStateManager;
128 
129     // Fields below are callbacks captured from test target.
130     private ContentObserver mShowRotationSuggestionsObserver;
131     private ContentObserver mAccelerometerRotationObserver;
132     private ContentObserver mUserRotationObserver;
133     private SensorEventListener mOrientationSensorListener;
134 
135     ArgumentCaptor<SensorEventListener> mHingeAngleSensorListenerCaptor = ArgumentCaptor.forClass(
136             SensorEventListener.class);
137 
138     private DisplayRotationBuilder mBuilder;
139 
140     private DeviceStateController mDeviceStateController;
141     private DisplayRotation mTarget;
142     @Nullable
143     private DisplayRotationImmersiveAppCompatPolicy mDisplayRotationImmersiveAppCompatPolicyMock;
144 
145     @BeforeClass
setUpOnce()146     public static void setUpOnce() {
147         sMockWm = mock(WindowManagerService.class);
148         sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
149         sMockWm.mPolicy = mock(WindowManagerPolicy.class);
150         sHandler = new TestHandler(null, sClock);
151     }
152 
153     @AfterClass
tearDownOnce()154     public static void tearDownOnce() {
155         // Make sure the fake settings are cleared after the last test method.
156         FakeSettingsProvider.clearSettingsProvider();
157     }
158 
159     @Before
setUp()160     public void setUp() {
161         FakeSettingsProvider.clearSettingsProvider();
162 
163         mPreviousStatusBarManagerInternal = LocalServices.getService(
164                 StatusBarManagerInternal.class);
165         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
166         mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class);
167         LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
168         mDisplayRotationImmersiveAppCompatPolicyMock = null;
169         mBuilder = new DisplayRotationBuilder();
170     }
171 
172     @After
tearDown()173     public void tearDown() {
174         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
175         if (mPreviousStatusBarManagerInternal != null) {
176             LocalServices.addService(StatusBarManagerInternal.class,
177                     mPreviousStatusBarManagerInternal);
178             mPreviousStatusBarManagerInternal = null;
179         }
180     }
181 
182     // ================================
183     // Display Settings Related Tests
184     // ================================
185     @Test
testLocksUserRotation_LockRotation_DefaultDisplay()186     public void testLocksUserRotation_LockRotation_DefaultDisplay() throws Exception {
187         mBuilder.build();
188 
189         freezeRotation(Surface.ROTATION_180);
190 
191         assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode());
192         assertEquals(Surface.ROTATION_180, mTarget.getUserRotation());
193 
194         assertEquals(0, Settings.System.getInt(mMockResolver,
195                 Settings.System.ACCELEROMETER_ROTATION));
196         assertEquals(Surface.ROTATION_180, Settings.System.getInt(mMockResolver,
197                 Settings.System.USER_ROTATION));
198     }
199 
200     @Test
testPersistsUserRotation_LockRotation_NonDefaultDisplay()201     public void testPersistsUserRotation_LockRotation_NonDefaultDisplay() throws Exception {
202         mBuilder.setIsDefaultDisplay(false).build();
203 
204         freezeRotation(Surface.ROTATION_180);
205 
206         assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode());
207         assertEquals(Surface.ROTATION_180, mTarget.getUserRotation());
208 
209         verify(mMockDisplayWindowSettings).setUserRotation(mMockDisplayContent,
210                 WindowManagerPolicy.USER_ROTATION_LOCKED, Surface.ROTATION_180);
211     }
212 
213     @Test
testPersistUserRotation_UnlockRotation_DefaultDisplay()214     public void testPersistUserRotation_UnlockRotation_DefaultDisplay() throws Exception {
215         mBuilder.build();
216 
217         thawRotation();
218 
219         assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode());
220 
221         assertEquals(1, Settings.System.getInt(mMockResolver,
222                 Settings.System.ACCELEROMETER_ROTATION));
223     }
224 
225     @Test
testPersistsUserRotation_UnlockRotation_NonDefaultDisplay()226     public void testPersistsUserRotation_UnlockRotation_NonDefaultDisplay() throws Exception {
227         mBuilder.setIsDefaultDisplay(false).build();
228 
229         thawRotation();
230 
231         assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode());
232 
233         verify(mMockDisplayWindowSettings).setUserRotation(same(mMockDisplayContent),
234                 eq(WindowManagerPolicy.USER_ROTATION_FREE), anyInt());
235     }
236 
237     @Test
testPersistsFixedToUserRotation()238     public void testPersistsFixedToUserRotation() throws Exception {
239         mBuilder.build();
240 
241         mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
242 
243         verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent,
244                 FIXED_TO_USER_ROTATION_ENABLED);
245 
246         reset(mMockDisplayWindowSettings);
247         mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_DISABLED);
248 
249         verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent,
250                 FIXED_TO_USER_ROTATION_DISABLED);
251 
252         reset(mMockDisplayWindowSettings);
253         mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_DEFAULT);
254 
255         verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent,
256                 FIXED_TO_USER_ROTATION_DEFAULT);
257     }
258 
259     // ========================================
260     // Tests for User Rotation based Rotation
261     // ========================================
262     @Test
testReturnsUserRotation_UserRotationLocked_NoAppRequest()263     public void testReturnsUserRotation_UserRotationLocked_NoAppRequest()
264             throws Exception {
265         mBuilder.build();
266         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
267 
268         freezeRotation(Surface.ROTATION_180);
269 
270         assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
271                 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
272     }
273 
274     @Test
testReturnsLandscape_UserRotationLockedSeascape_AppRequestsLandscape()275     public void testReturnsLandscape_UserRotationLockedSeascape_AppRequestsLandscape()
276             throws Exception {
277         mBuilder.build();
278         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */,
279                 false /* isTv */);
280 
281         freezeRotation(Surface.ROTATION_180);
282 
283         assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
284                 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90));
285     }
286 
287     @Test
testReturnsSeascape_UserRotationLockedSeascape_AppRequestsSeascape()288     public void testReturnsSeascape_UserRotationLockedSeascape_AppRequestsSeascape()
289             throws Exception {
290         mBuilder.build();
291         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */,
292                 false /* isTv */);
293 
294         freezeRotation(Surface.ROTATION_180);
295 
296         assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
297                 ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE, Surface.ROTATION_90));
298     }
299 
300     @Test
testReturnsPortrait_UserRotationLockedPortrait_AppRequestsPortrait()301     public void testReturnsPortrait_UserRotationLockedPortrait_AppRequestsPortrait()
302             throws Exception {
303         mBuilder.build();
304         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */,
305                 false /* isTv */);
306 
307         freezeRotation(Surface.ROTATION_270);
308 
309         assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
310                 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_0));
311     }
312 
313     @Test
testReturnsUpsideDown_UserRotationLockedUpsideDown_AppRequestsUpsideDown()314     public void testReturnsUpsideDown_UserRotationLockedUpsideDown_AppRequestsUpsideDown()
315             throws Exception {
316         mBuilder.build();
317         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */,
318                 false /* isTv */);
319 
320         freezeRotation(Surface.ROTATION_90);
321 
322         assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
323                 ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT, Surface.ROTATION_0));
324     }
325 
326     @Test
testReturnsSideways_UserRotationLocked_IncompatibleAppRequest()327     public void testReturnsSideways_UserRotationLocked_IncompatibleAppRequest()
328             throws Exception {
329         mBuilder.build();
330         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
331 
332         freezeRotation(Surface.ROTATION_180);
333 
334         final int rotation = mTarget.rotationForOrientation(
335                 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_90);
336         assertTrue("Rotation should be sideways, but it's "
337                         + Surface.rotationToString(rotation),
338                 rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
339     }
340 
341     // =================================
342     // Tests for Sensor based Rotation
343     // =================================
verifyOrientationListenerRegistration(int numOfInvocation)344     private void verifyOrientationListenerRegistration(int numOfInvocation) {
345         final ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(
346                 SensorEventListener.class);
347         waitForUiHandler();
348         verify(mMockSensorManager, times(numOfInvocation)).registerListener(
349                 listenerCaptor.capture(),
350                 same(mFakeOrientationSensor),
351                 anyInt(),
352                 any());
353         if (numOfInvocation > 0) {
354             mOrientationSensorListener = listenerCaptor.getValue();
355         }
356     }
357 
358     @Test
testNotEnablesSensor_AutoRotationNotSupported()359     public void testNotEnablesSensor_AutoRotationNotSupported() throws Exception {
360         mBuilder.setSupportAutoRotation(false).build();
361         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
362 
363         thawRotation();
364 
365         when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
366         when(mMockDisplayPolicy.isAwake()).thenReturn(true);
367         when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
368         when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
369         mTarget.updateOrientationListener();
370         verifyOrientationListenerRegistration(0);
371     }
372 
373     @Test
testNotEnablesSensor_ScreenNotOn()374     public void testNotEnablesSensor_ScreenNotOn() throws Exception {
375         mBuilder.build();
376         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
377 
378         thawRotation();
379 
380         when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(false);
381         when(mMockDisplayPolicy.isAwake()).thenReturn(true);
382         when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
383         when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
384         mTarget.updateOrientationListener();
385         verifyOrientationListenerRegistration(0);
386     }
387 
388     @Test
testNotEnablesSensor_NotAwake()389     public void testNotEnablesSensor_NotAwake() throws Exception {
390         mBuilder.build();
391         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
392 
393         thawRotation();
394 
395         when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
396         when(mMockDisplayPolicy.isAwake()).thenReturn(false);
397         when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
398         when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
399         mTarget.updateOrientationListener();
400         verifyOrientationListenerRegistration(0);
401     }
402 
403     @Test
testNotEnablesSensor_KeyguardNotDrawnCompletely()404     public void testNotEnablesSensor_KeyguardNotDrawnCompletely() throws Exception {
405         mBuilder.build();
406         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
407 
408         thawRotation();
409 
410         when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
411         when(mMockDisplayPolicy.isAwake()).thenReturn(true);
412         when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(false);
413         when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
414         mTarget.updateOrientationListener();
415         verifyOrientationListenerRegistration(0);
416     }
417 
418     @Test
testNotEnablesSensor_WindowManagerNotDrawnCompletely()419     public void testNotEnablesSensor_WindowManagerNotDrawnCompletely() throws Exception {
420         mBuilder.build();
421         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
422 
423         thawRotation();
424 
425         when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
426         when(mMockDisplayPolicy.isAwake()).thenReturn(true);
427         when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
428         when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(false);
429         mTarget.updateOrientationListener();
430         verifyOrientationListenerRegistration(0);
431     }
432 
433     @Test
testNotEnablesSensor_FixedUserRotation()434     public void testNotEnablesSensor_FixedUserRotation() throws Exception {
435         mBuilder.build();
436         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
437 
438         when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
439         when(mMockDisplayPolicy.isAwake()).thenReturn(true);
440         when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
441         when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
442         mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
443         mTarget.updateOrientationListener();
444         verifyOrientationListenerRegistration(0);
445     }
446 
447     @Test
testNotEnablesSensor_ForceDefaultRotation_Car()448     public void testNotEnablesSensor_ForceDefaultRotation_Car() throws Exception {
449         mBuilder.build();
450         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
451 
452         when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
453         when(mMockDisplayPolicy.isAwake()).thenReturn(true);
454         when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
455         when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
456         mTarget.updateOrientationListener();
457         verifyOrientationListenerRegistration(0);
458     }
459 
460     @Test
testNotEnablesSensor_ForceDefaultRotation_Tv()461     public void testNotEnablesSensor_ForceDefaultRotation_Tv() throws Exception {
462         mBuilder.build();
463         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
464 
465         when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
466         when(mMockDisplayPolicy.isAwake()).thenReturn(true);
467         when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
468         when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
469         mTarget.updateOrientationListener();
470         verifyOrientationListenerRegistration(0);
471     }
472 
enableOrientationSensor()473     private void enableOrientationSensor() {
474         when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
475         when(mMockDisplayPolicy.isAwake()).thenReturn(true);
476         when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
477         when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
478         mTarget.updateOrientationListener();
479         verifyOrientationListenerRegistration(1);
480     }
481 
createSensorEvent(int rotation)482     private SensorEvent createSensorEvent(int rotation) throws Exception {
483         return createSensorEvent(mFakeOrientationSensor, rotation);
484     }
485 
createSensorEvent(Sensor sensor, int value)486     private SensorEvent createSensorEvent(Sensor sensor, int value) throws Exception {
487         final Constructor<SensorEvent> constructor =
488                 SensorEvent.class.getDeclaredConstructor(int.class);
489         constructor.setAccessible(true);
490         final SensorEvent event = constructor.newInstance(1);
491         event.sensor = sensor;
492         event.values[0] = value;
493         event.timestamp = SystemClock.elapsedRealtimeNanos();
494         return event;
495     }
496 
497     @Test
testReturnsSensorRotation_RotationThawed()498     public void testReturnsSensorRotation_RotationThawed() throws Exception {
499         mBuilder.build();
500         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
501 
502         thawRotation();
503 
504         enableOrientationSensor();
505 
506         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
507 
508         assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
509                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
510     }
511 
512     @Test
testReverseRotation()513     public void testReverseRotation() throws Exception {
514         mBuilder.build();
515         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
516 
517         when(mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()).thenReturn(true);
518 
519         thawRotation();
520 
521         enableOrientationSensor();
522 
523         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
524         assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
525                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
526 
527         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_270));
528         assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
529                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
530 
531         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
532         assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
533                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
534 
535         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
536         assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
537                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_180));
538     }
539 
waitForUiHandler()540     private boolean waitForUiHandler() {
541         final CountDownLatch latch = new CountDownLatch(1);
542         UiThread.getHandler().post(latch::countDown);
543         try {
544             return latch.await(UI_HANDLER_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
545         } catch (InterruptedException ignored) {
546         }
547         throw new AssertionError("Failed to wait for ui handler");
548     }
549 
550     @Test
testUpdatesRotationWhenSensorUpdates_RotationThawed()551     public void testUpdatesRotationWhenSensorUpdates_RotationThawed() throws Exception {
552         mBuilder.build();
553         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
554 
555         thawRotation();
556 
557         enableOrientationSensor();
558 
559         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
560         assertTrue(waitForUiHandler());
561 
562         verify(sMockWm).updateRotation(false, false);
563     }
564 
565     @Test
testNotifiesChoiceWhenSensorUpdates_RotationLocked()566     public void testNotifiesChoiceWhenSensorUpdates_RotationLocked() throws Exception {
567         mBuilder.build();
568         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
569 
570         freezeRotation(Surface.ROTATION_270);
571 
572         enableOrientationSensor();
573 
574         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
575         assertTrue(waitForUiHandler());
576 
577         verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true);
578     }
579 
580     @Test
testNotifiesChoiceWhenSensorUpdates_immersiveApp()581     public void testNotifiesChoiceWhenSensorUpdates_immersiveApp() throws Exception {
582         mDisplayRotationImmersiveAppCompatPolicyMock = mock(
583                 DisplayRotationImmersiveAppCompatPolicy.class);
584         when(mDisplayRotationImmersiveAppCompatPolicyMock.isRotationLockEnforced(
585                 Surface.ROTATION_90)).thenReturn(true);
586 
587         mBuilder.build();
588         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
589 
590         thawRotation();
591 
592         enableOrientationSensor();
593 
594         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
595         assertTrue(waitForUiHandler());
596 
597         verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true);
598 
599         // An imaginary ActivityRecord.setRequestedOrientation call disables immersive mode:
600         when(mDisplayRotationImmersiveAppCompatPolicyMock.isRotationLockEnforced(
601                 Surface.ROTATION_90)).thenReturn(false);
602 
603         // And then ActivityRecord.setRequestedOrientation calls onSetRequestedOrientation.
604         mTarget.onSetRequestedOrientation();
605 
606         // onSetRequestedOrientation should lead to a second call to
607         // mOrientationListener.onProposedRotationChanged
608         // but now, instead of notifying mMockStatusBarManagerInternal, it calls updateRotation:
609         verify(sMockWm).updateRotation(false, false);
610     }
611 
612     @Test
testAllowAllRotations_allowsUpsideDownSuggestion()613     public void testAllowAllRotations_allowsUpsideDownSuggestion()
614             throws Exception {
615         mBuilder.build();
616         mTarget.updateOrientation(SCREEN_ORIENTATION_UNSPECIFIED, true);
617         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
618         when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
619                 .thenReturn(true);
620         freezeRotation(Surface.ROTATION_0);
621         enableOrientationSensor();
622 
623         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
624         assertTrue(waitForUiHandler());
625 
626         verify(mMockStatusBarManagerInternal)
627                 .onProposedRotationChanged(Surface.ROTATION_180, true);
628     }
629 
630     @Test
testDoNotAllowAllRotations_doesNotAllowUpsideDownSuggestion()631     public void testDoNotAllowAllRotations_doesNotAllowUpsideDownSuggestion()
632             throws Exception {
633         mBuilder.build();
634         mTarget.updateOrientation(SCREEN_ORIENTATION_UNSPECIFIED, true);
635         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
636         when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
637                 .thenReturn(false);
638         freezeRotation(Surface.ROTATION_0);
639         enableOrientationSensor();
640 
641         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
642         assertTrue(waitForUiHandler());
643 
644         verify(mMockStatusBarManagerInternal)
645                 .onProposedRotationChanged(Surface.ROTATION_180, false);
646     }
647 
648     @Test
testAllowAllRotations_allowAllRotationsBecomesDisabled_forbidsUpsideDownSuggestion()649     public void testAllowAllRotations_allowAllRotationsBecomesDisabled_forbidsUpsideDownSuggestion()
650             throws Exception {
651         mBuilder.build();
652         mTarget.updateOrientation(SCREEN_ORIENTATION_UNSPECIFIED, true);
653         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
654         when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
655                 .thenReturn(true);
656         freezeRotation(Surface.ROTATION_0);
657         enableOrientationSensor();
658         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
659         assertTrue(waitForUiHandler());
660 
661         // Change resource to disallow all rotations.
662         // Reset "allowAllRotations".
663         mTarget.applyCurrentRotation(Surface.ROTATION_0);
664         clearInvocations(mMockStatusBarManagerInternal);
665         when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
666                 .thenReturn(false);
667         mTarget.resetAllowAllRotations();
668         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
669         assertTrue(waitForUiHandler());
670 
671         verify(mMockStatusBarManagerInternal)
672                 .onProposedRotationChanged(Surface.ROTATION_180, false);
673     }
674 
675     @Test
testReturnsCompatibleRotation_SensorEnabled_RotationThawed()676     public void testReturnsCompatibleRotation_SensorEnabled_RotationThawed() throws Exception {
677         mBuilder.build();
678         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
679 
680         thawRotation();
681 
682         enableOrientationSensor();
683 
684         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
685 
686         final int rotation = mTarget.rotationForOrientation(SCREEN_ORIENTATION_LANDSCAPE,
687                 Surface.ROTATION_0);
688         assertTrue("Rotation should be sideways but it's "
689                 + Surface.rotationToString(rotation),
690                 rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
691     }
692 
693     @Test
testReturnsUserRotation_SensorEnabled_RotationLocked()694     public void testReturnsUserRotation_SensorEnabled_RotationLocked() throws Exception {
695         mBuilder.build();
696         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
697 
698         freezeRotation(Surface.ROTATION_270);
699 
700         enableOrientationSensor();
701 
702         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
703 
704         assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
705                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
706     }
707 
708     @Test
testReturnsSensorRotation_180degrees_allRotationsAllowed()709     public void testReturnsSensorRotation_180degrees_allRotationsAllowed()
710             throws Exception {
711         mBuilder.build();
712         when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
713                 .thenReturn(true);
714         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
715         enableOrientationSensor();
716         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
717 
718         assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
719                 SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0));
720     }
721 
722     @Test
testReturnLastRotation_sensor180_allRotationsNotAllowed()723     public void testReturnLastRotation_sensor180_allRotationsNotAllowed()
724             throws Exception {
725         mBuilder.build();
726         when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
727                 .thenReturn(false);
728         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
729         enableOrientationSensor();
730         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
731 
732         assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
733                 SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0));
734     }
735 
736     @Test
testAllowRotationsIsCached()737     public void testAllowRotationsIsCached()
738             throws Exception {
739         mBuilder.build();
740         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
741         enableOrientationSensor();
742 
743         // Rotate once to read the resource
744         when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
745                 .thenReturn(true);
746         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
747         mTarget.rotationForOrientation(SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0);
748 
749         // Change resource to disallow all rotations.
750         // Rotate again and 180 degrees rotation should still be returned even if "disallowed".
751         when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
752                 .thenReturn(false);
753         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
754         assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
755                 SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0));
756     }
757 
758     @Test
testResetAllowRotations()759     public void testResetAllowRotations()
760             throws Exception {
761         mBuilder.build();
762         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
763         enableOrientationSensor();
764 
765         // Rotate once to read the resource
766         when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
767                 .thenReturn(true);
768         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
769         mTarget.rotationForOrientation(SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0);
770 
771         // Change resource to disallow all rotations.
772         // Reset "allowAllRotations".
773         // Rotate again and 180 degrees rotation should not be allowed anymore.
774         when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
775                 .thenReturn(false);
776         mTarget.resetAllowAllRotations();
777         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
778         assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
779                 SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0));
780     }
781 
782     // ====================================================
783     // Tests for half-fold auto-rotate override of rotation
784     // ====================================================
785     @Test
testUpdatesRotationWhenSensorUpdates_RotationLocked_HalfFolded()786     public void testUpdatesRotationWhenSensorUpdates_RotationLocked_HalfFolded() throws Exception {
787         mBuilder.setSupportHalfFoldAutoRotateOverride(true);
788         mBuilder.build();
789         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
790 
791         enableOrientationSensor();
792 
793         mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
794         freezeRotation(Surface.ROTATION_270);
795 
796         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
797         assertTrue(waitForUiHandler());
798         // No rotation...
799         assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
800                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
801 
802         // ... until half-fold
803         mTarget.foldStateChanged(DeviceStateController.DeviceState.HALF_FOLDED);
804         assertTrue(waitForUiHandler());
805         verify(sMockWm).updateRotation(false, false);
806         assertTrue(waitForUiHandler());
807         assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
808                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
809 
810         // ... then transition back to flat
811         mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
812         assertTrue(waitForUiHandler());
813         verify(sMockWm, atLeast(1)).updateRotation(false, false);
814         assertTrue(waitForUiHandler());
815         assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
816                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
817     }
818 
819     // =================================
820     // Tests for Policy based Rotation
821     // =================================
822     @Test
testReturnsUserRotation_ForceDefaultRotation_Car()823     public void testReturnsUserRotation_ForceDefaultRotation_Car() throws Exception {
824         mBuilder.build();
825         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
826 
827         assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
828                 Surface.ROTATION_180));
829     }
830 
831     @Test
testReturnsUserRotation_ForceDefaultRotation_Tv()832     public void testReturnsUserRotation_ForceDefaultRotation_Tv() throws Exception {
833         mBuilder.build();
834         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
835 
836         assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
837                 Surface.ROTATION_180));
838     }
839 
840     @Test
testReturnsLidOpenRotation_LidOpen()841     public void testReturnsLidOpenRotation_LidOpen() throws Exception {
842         mBuilder.setLidOpenRotation(Surface.ROTATION_90).build();
843         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
844 
845         when(mMockDisplayPolicy.getLidState()).thenReturn(
846                 WindowManagerPolicy.WindowManagerFuncs.LID_OPEN);
847 
848         freezeRotation(Surface.ROTATION_270);
849 
850         assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
851                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
852     }
853 
854     @Test
testReturnsCarDockRotation_CarDockedMode()855     public void testReturnsCarDockRotation_CarDockedMode() throws Exception {
856         mBuilder.setCarDockRotation(Surface.ROTATION_270).build();
857         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
858 
859         when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_CAR);
860 
861         freezeRotation(Surface.ROTATION_90);
862 
863         assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
864                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
865     }
866 
867     @Test
testReturnsDeskDockRotation_DeskDockedMode()868     public void testReturnsDeskDockRotation_DeskDockedMode() throws Exception {
869         mBuilder.setDeskDockRotation(Surface.ROTATION_270).build();
870         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
871 
872         when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_DESK);
873 
874         freezeRotation(Surface.ROTATION_90);
875 
876         assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
877                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
878     }
879 
880     @Test
testIgnoresDeskDockRotation_whenNoSensorAndLockedRespected()881     public void testIgnoresDeskDockRotation_whenNoSensorAndLockedRespected() throws Exception {
882         mBuilder.setDeskDockRotation(Surface.ROTATION_270).build();
883         when(mMockDisplayPolicy.isDeskDockRespectsNoSensorAndLockedWithoutAccelerometer())
884                 .thenReturn(true);
885         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
886 
887         when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_DESK);
888 
889         freezeRotation(Surface.ROTATION_90);
890 
891         assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
892                 SCREEN_ORIENTATION_LOCKED, Surface.ROTATION_90));
893         assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
894                 SCREEN_ORIENTATION_NOSENSOR, Surface.ROTATION_90));
895     }
896 
897     @Test
testReturnsUserRotation_FixedToUserRotation_IgnoreIncompatibleAppRequest()898     public void testReturnsUserRotation_FixedToUserRotation_IgnoreIncompatibleAppRequest()
899             throws Exception {
900         mBuilder.build();
901         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
902 
903         mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
904 
905         freezeRotation(Surface.ROTATION_180);
906 
907         final int rotation = mTarget.rotationForOrientation(
908                 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90);
909         assertEquals(Surface.ROTATION_180, rotation);
910     }
911 
912     @Test
testReturnsUserRotation_NonDefaultDisplay()913     public void testReturnsUserRotation_NonDefaultDisplay() throws Exception {
914         mBuilder.setIsDefaultDisplay(false).build();
915         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
916 
917         freezeRotation(Surface.ROTATION_90);
918 
919         assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
920                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
921     }
922 
923     @Test
testShouldRotateSeamlessly()924     public void testShouldRotateSeamlessly() throws Exception {
925         mBuilder.build();
926 
927         final WindowState win = mock(WindowState.class);
928         win.mToken = win.mActivityRecord = mock(ActivityRecord.class);
929         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
930         attrs.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
931 
932         doReturn(attrs).when(win).getAttrs();
933         doReturn(true).when(mMockDisplayPolicy).navigationBarCanMove();
934         doReturn(win).when(mMockDisplayPolicy).getTopFullscreenOpaqueWindow();
935         mMockDisplayContent.mCurrentFocus = win;
936         // This should not affect the condition of shouldRotateSeamlessly.
937         mTarget.mUpsideDownRotation = Surface.ROTATION_90;
938 
939         doReturn(true).when(win.mActivityRecord).matchParentBounds();
940         // The focused fullscreen opaque window without override bounds should be able to be
941         // rotated seamlessly.
942         assertTrue(mTarget.shouldRotateSeamlessly(
943                 Surface.ROTATION_0, Surface.ROTATION_90, false /* forceUpdate */));
944         // Reject any 180 degree because non-movable navbar will be placed in a different position.
945         doReturn(false).when(mMockDisplayPolicy).navigationBarCanMove();
946         assertFalse(mTarget.shouldRotateSeamlessly(
947                 Surface.ROTATION_90, Surface.ROTATION_180, false /* forceUpdate */));
948 
949         doReturn(true).when(mMockDisplayPolicy).navigationBarCanMove();
950         doReturn(false).when(win.mActivityRecord).matchParentBounds();
951         // No seamless rotation if the window may be positioned with offset after rotation.
952         assertFalse(mTarget.shouldRotateSeamlessly(
953                 Surface.ROTATION_0, Surface.ROTATION_90, false /* forceUpdate */));
954     }
955 
956     @Test
testSensorRotationAfterDisplayChangeBeforeTimeout_ignoresSensor()957     public void testSensorRotationAfterDisplayChangeBeforeTimeout_ignoresSensor() throws Exception {
958         mBuilder.setSupportHalfFoldAutoRotateOverride(true)
959                 .setPauseRotationWhenUnfolding(true)
960                 .setDisplaySwitchRotationBlockTimeMs(1000)
961                 .build();
962         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
963         thawRotation();
964         enableOrientationSensor();
965 
966         mTarget.physicalDisplayChanged();
967 
968         moveTimeForward(900);
969         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
970         assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
971                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
972     }
973 
974     @Test
testSensorRotationAfterDisplayChangeAfterTimeout_usesSensor()975     public void testSensorRotationAfterDisplayChangeAfterTimeout_usesSensor() throws Exception {
976         mBuilder.setSupportHalfFoldAutoRotateOverride(true)
977                 .setPauseRotationWhenUnfolding(true)
978                 .setDisplaySwitchRotationBlockTimeMs(1000)
979                 .build();
980         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
981         thawRotation();
982         enableOrientationSensor();
983 
984         mTarget.physicalDisplayChanged();
985 
986         moveTimeForward(1100);
987         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
988         assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
989                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
990     }
991 
992     @Test
testSensorRotationAfterHingeEventBeforeTimeout_ignoresSensor()993     public void testSensorRotationAfterHingeEventBeforeTimeout_ignoresSensor() throws Exception {
994         mBuilder.setSupportHalfFoldAutoRotateOverride(true)
995                 .setPauseRotationWhenUnfolding(true)
996                 .setMaxHingeAngle(165)
997                 .setHingeAngleRotationBlockTimeMs(400)
998                 .build();
999         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
1000         thawRotation();
1001         enableOrientationSensor();
1002 
1003         sendHingeAngleEvent(130);
1004 
1005         moveTimeForward( 300);
1006         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
1007         assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
1008                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
1009     }
1010 
1011     @Test
testSensorRotationAfterHingeEventBeforeTimeoutFlagDisabled_usesSensorData()1012     public void testSensorRotationAfterHingeEventBeforeTimeoutFlagDisabled_usesSensorData()
1013             throws Exception {
1014         mBuilder.setSupportHalfFoldAutoRotateOverride(true)
1015                 .setPauseRotationWhenUnfolding(false)
1016                 .setMaxHingeAngle(165)
1017                 .setHingeAngleRotationBlockTimeMs(400)
1018                 .build();
1019         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
1020         thawRotation();
1021         enableOrientationSensor();
1022 
1023         sendHingeAngleEvent(130);
1024 
1025         moveTimeForward( 300);
1026         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
1027         assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
1028                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
1029     }
1030 
1031     @Test
testSensorRotationAfterHingeEventAfterTimeout_usesSensorData()1032     public void testSensorRotationAfterHingeEventAfterTimeout_usesSensorData() throws Exception {
1033         mBuilder.setSupportHalfFoldAutoRotateOverride(true)
1034                 .setPauseRotationWhenUnfolding(true)
1035                 .setMaxHingeAngle(165)
1036                 .setHingeAngleRotationBlockTimeMs(400)
1037                 .build();
1038         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
1039         thawRotation();
1040         enableOrientationSensor();
1041 
1042         sendHingeAngleEvent(180);
1043 
1044         moveTimeForward(1010);
1045         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
1046         assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
1047                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
1048     }
1049 
1050 
1051     @Test
testSensorRotationAfterLargeHingeEventBeforeTimeout_usesSensor()1052     public void testSensorRotationAfterLargeHingeEventBeforeTimeout_usesSensor() throws Exception {
1053         mBuilder.setSupportHalfFoldAutoRotateOverride(true)
1054                 .setPauseRotationWhenUnfolding(true)
1055                 .setMaxHingeAngle(165)
1056                 .setHingeAngleRotationBlockTimeMs(400)
1057                 .build();
1058         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
1059         thawRotation();
1060         enableOrientationSensor();
1061 
1062         sendHingeAngleEvent(180);
1063 
1064         moveTimeForward(300);
1065         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
1066         assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
1067                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
1068     }
1069 
1070     // ========================
1071     // Non-rotation API Tests
1072     // ========================
1073     @Test
testIsNotFixedToUserRotationByDefault()1074     public void testIsNotFixedToUserRotationByDefault() throws Exception {
1075         mBuilder.build();
1076 
1077         assertFalse("Display rotation should respect app requested orientation by"
1078                 + " default.", mTarget.isFixedToUserRotation());
1079     }
1080 
1081     @Test
testIsFixedToUserRotation()1082     public void testIsFixedToUserRotation() throws Exception {
1083         mBuilder.build();
1084         mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
1085 
1086         assertTrue("Display rotation shouldn't respect app requested orientation if"
1087                 + " fixed to user rotation.", mTarget.isFixedToUserRotation());
1088     }
1089 
moveTimeForward(long timeMillis)1090     private void moveTimeForward(long timeMillis) {
1091         sCurrentUptimeMillis += timeMillis;
1092         sClock.fastForward(timeMillis);
1093         sHandler.timeAdvance();
1094     }
1095 
1096     /**
1097      * Call {@link DisplayRotation#configure(int, int)} to configure {@link #mTarget}
1098      * according to given parameters.
1099      */
configureDisplayRotation(int displayOrientation, boolean isCar, boolean isTv)1100     private void configureDisplayRotation(int displayOrientation, boolean isCar, boolean isTv) {
1101         final int width;
1102         final int height;
1103         switch (displayOrientation) {
1104             case SCREEN_ORIENTATION_LANDSCAPE:
1105                 width = 1920;
1106                 height = 1080;
1107                 break;
1108             case SCREEN_ORIENTATION_PORTRAIT:
1109                 width = 1080;
1110                 height = 1920;
1111                 break;
1112             default:
1113                 throw new IllegalArgumentException("displayOrientation needs to be either landscape"
1114                         + " or portrait, but we got "
1115                         + ActivityInfo.screenOrientationToString(displayOrientation));
1116         }
1117 
1118         final PackageManager mockPackageManager = mock(PackageManager.class);
1119         when(mMockContext.getPackageManager()).thenReturn(mockPackageManager);
1120         when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
1121                 .thenReturn(isCar);
1122         when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
1123                 .thenReturn(isTv);
1124 
1125         mTarget.configure(width, height);
1126     }
1127 
sendHingeAngleEvent(int hingeAngle)1128     private void sendHingeAngleEvent(int hingeAngle) {
1129         mHingeAngleSensorListenerCaptor.getAllValues().forEach(sensorEventListener -> {
1130             try {
1131                 sensorEventListener.onSensorChanged(createSensorEvent(mFakeHingeAngleSensor,
1132                             hingeAngle));
1133             } catch (Exception e) {
1134                 throw new RuntimeException(e);
1135             }
1136         });
1137     }
1138 
freezeRotation(int rotation)1139     private void freezeRotation(int rotation) {
1140         mTarget.freezeRotation(rotation);
1141 
1142         if (mTarget.isDefaultDisplay) {
1143             mAccelerometerRotationObserver.onChange(false);
1144             mUserRotationObserver.onChange(false);
1145         }
1146     }
1147 
thawRotation()1148     private void thawRotation() {
1149         mTarget.thawRotation();
1150 
1151         if (mTarget.isDefaultDisplay) {
1152             mAccelerometerRotationObserver.onChange(false);
1153             mUserRotationObserver.onChange(false);
1154         }
1155     }
1156 
1157     private class DisplayRotationBuilder {
1158         private boolean mIsDefaultDisplay = true;
1159         private boolean mSupportAutoRotation = true;
1160         private boolean mPauseRotationWhenUnfolding = false;
1161         private boolean mSupportHalfFoldAutoRotateOverride = false;
1162         private int mDisplaySwitchRotationBlockTimeMs;
1163         private int mHingeAngleRotationBlockTimeMs;
1164         private int mMaxHingeAngle;
1165 
1166         private int mLidOpenRotation = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
1167         private int mCarDockRotation;
1168         private int mDeskDockRotation;
1169         private int mUndockedHdmiRotation;
1170 
setIsDefaultDisplay(boolean isDefaultDisplay)1171         private DisplayRotationBuilder setIsDefaultDisplay(boolean isDefaultDisplay) {
1172             mIsDefaultDisplay = isDefaultDisplay;
1173             return this;
1174         }
1175 
setPauseRotationWhenUnfolding( boolean pauseRotationWhenUnfolding)1176         public DisplayRotationBuilder setPauseRotationWhenUnfolding(
1177                 boolean pauseRotationWhenUnfolding) {
1178             mPauseRotationWhenUnfolding = pauseRotationWhenUnfolding;
1179             return this;
1180         }
1181 
setDisplaySwitchRotationBlockTimeMs( int displaySwitchRotationBlockTimeMs)1182         public DisplayRotationBuilder setDisplaySwitchRotationBlockTimeMs(
1183                 int displaySwitchRotationBlockTimeMs) {
1184             mDisplaySwitchRotationBlockTimeMs = displaySwitchRotationBlockTimeMs;
1185             return this;
1186         }
1187 
setHingeAngleRotationBlockTimeMs( int hingeAngleRotationBlockTimeMs)1188         public DisplayRotationBuilder setHingeAngleRotationBlockTimeMs(
1189                 int hingeAngleRotationBlockTimeMs) {
1190             mHingeAngleRotationBlockTimeMs = hingeAngleRotationBlockTimeMs;
1191             return this;
1192         }
1193 
setMaxHingeAngle(int maxHingeAngle)1194         public DisplayRotationBuilder setMaxHingeAngle(int maxHingeAngle) {
1195             mMaxHingeAngle = maxHingeAngle;
1196             return this;
1197         }
1198 
setSupportAutoRotation(boolean supportAutoRotation)1199         private DisplayRotationBuilder setSupportAutoRotation(boolean supportAutoRotation) {
1200             mSupportAutoRotation = supportAutoRotation;
1201             return this;
1202         }
1203 
setLidOpenRotation(int rotation)1204         private DisplayRotationBuilder setLidOpenRotation(int rotation) {
1205             mLidOpenRotation = rotation;
1206             return this;
1207         }
1208 
setCarDockRotation(int rotation)1209         private DisplayRotationBuilder setCarDockRotation(int rotation) {
1210             mCarDockRotation = rotation;
1211             return this;
1212         }
1213 
setDeskDockRotation(int rotation)1214         private DisplayRotationBuilder setDeskDockRotation(int rotation) {
1215             mDeskDockRotation = rotation;
1216             return this;
1217         }
1218 
setUndockedHdmiRotation(int rotation)1219         private DisplayRotationBuilder setUndockedHdmiRotation(int rotation) {
1220             mUndockedHdmiRotation = rotation;
1221             return this;
1222         }
1223 
setSupportHalfFoldAutoRotateOverride( boolean supportHalfFoldAutoRotateOverride)1224         private DisplayRotationBuilder setSupportHalfFoldAutoRotateOverride(
1225                 boolean supportHalfFoldAutoRotateOverride) {
1226             mSupportHalfFoldAutoRotateOverride = supportHalfFoldAutoRotateOverride;
1227             return this;
1228         }
1229 
captureObservers()1230         private void captureObservers() {
1231             ArgumentCaptor<ContentObserver> captor = ArgumentCaptor.forClass(
1232                     ContentObserver.class);
1233             verify(mMockResolver, atMost(1)).registerContentObserver(
1234                     eq(Settings.Secure.getUriFor(Settings.Secure.SHOW_ROTATION_SUGGESTIONS)),
1235                     anyBoolean(),
1236                     captor.capture(),
1237                     anyInt());
1238             if (!captor.getAllValues().isEmpty()) {
1239                 mShowRotationSuggestionsObserver = captor.getValue();
1240             }
1241 
1242             captor = ArgumentCaptor.forClass(ContentObserver.class);
1243             verify(mMockResolver, atMost(1)).registerContentObserver(
1244                     eq(Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION)),
1245                     anyBoolean(),
1246                     captor.capture(),
1247                     anyInt());
1248             if (!captor.getAllValues().isEmpty()) {
1249                 mAccelerometerRotationObserver = captor.getValue();
1250             }
1251 
1252             captor = ArgumentCaptor.forClass(ContentObserver.class);
1253             verify(mMockResolver, atMost(1)).registerContentObserver(
1254                     eq(Settings.System.getUriFor(Settings.System.USER_ROTATION)),
1255                     anyBoolean(),
1256                     captor.capture(),
1257                     anyInt());
1258             if (!captor.getAllValues().isEmpty()) {
1259                 mUserRotationObserver = captor.getValue();
1260             }
1261         }
1262 
createSensor(int type)1263         private Sensor createSensor(int type) throws Exception {
1264             Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
1265             constr.setAccessible(true);
1266             Sensor sensor = constr.newInstance();
1267 
1268             setSensorType(sensor, type);
1269             setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type);
1270             setSensorField(sensor, "mVendor", "Mock Vendor");
1271             setSensorField(sensor, "mVersion", 1);
1272             setSensorField(sensor, "mHandle", -1);
1273             setSensorField(sensor, "mMaxRange", 10);
1274             setSensorField(sensor, "mResolution", 1);
1275             setSensorField(sensor, "mPower", 1);
1276             setSensorField(sensor, "mMinDelay", 1000);
1277             setSensorField(sensor, "mMaxDelay", 1000000000);
1278             setSensorField(sensor, "mFlags", 0);
1279             setSensorField(sensor, "mId", -1);
1280 
1281             return sensor;
1282         }
1283 
setSensorType(Sensor sensor, int type)1284         private void setSensorType(Sensor sensor, int type) throws Exception {
1285             Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
1286             setter.setAccessible(true);
1287             setter.invoke(sensor, type);
1288         }
1289 
setSensorField(Sensor sensor, String fieldName, Object value)1290         private void setSensorField(Sensor sensor, String fieldName, Object value)
1291                 throws Exception {
1292             Field field = Sensor.class.getDeclaredField(fieldName);
1293             field.setAccessible(true);
1294             field.set(sensor, value);
1295         }
1296 
convertRotationToDegrees(@urface.Rotation int rotation)1297         private int convertRotationToDegrees(@Surface.Rotation int rotation) {
1298             switch (rotation) {
1299                 case Surface.ROTATION_0:
1300                     return 0;
1301                 case Surface.ROTATION_90:
1302                     return 90;
1303                 case Surface.ROTATION_180:
1304                     return 180;
1305                 case Surface.ROTATION_270:
1306                     return 270;
1307                 default:
1308                     return -1;
1309             }
1310         }
1311 
build()1312         private void build() throws Exception {
1313             mMockContext = mock(Context.class);
1314 
1315             mMockDisplayContent = mock(DisplayContent.class);
1316             mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay;
1317             when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt()))
1318                     .thenReturn(NO_CUTOUT);
1319             when(mMockDisplayContent.getDefaultTaskDisplayArea())
1320                     .thenReturn(mock(TaskDisplayArea.class));
1321             when(mMockDisplayContent.getWindowConfiguration())
1322                     .thenReturn(new WindowConfiguration());
1323 
1324             Field field = DisplayContent.class
1325                     .getDeclaredField("mFixedRotationTransitionListener");
1326             field.setAccessible(true);
1327             field.set(mMockDisplayContent, mock(FixedRotationTransitionListener.class));
1328 
1329             mMockDisplayPolicy = mock(DisplayPolicy.class);
1330 
1331             mMockRes = mock(Resources.class);
1332             when(mMockContext.getResources()).thenReturn((mMockRes));
1333             when(mMockRes.getBoolean(com.android.internal.R.bool
1334                     .config_windowManagerPauseRotationWhenUnfolding))
1335                     .thenReturn(mPauseRotationWhenUnfolding);
1336             when(mMockRes.getInteger(com.android.internal.R.integer
1337                     .config_pauseRotationWhenUnfolding_displaySwitchTimeout))
1338                     .thenReturn(mDisplaySwitchRotationBlockTimeMs);
1339             when(mMockRes.getInteger(com.android.internal.R.integer
1340                     .config_pauseRotationWhenUnfolding_hingeEventTimeout))
1341                     .thenReturn(mHingeAngleRotationBlockTimeMs);
1342             when(mMockRes.getInteger(com.android.internal.R.integer
1343                     .config_pauseRotationWhenUnfolding_maxHingeAngle))
1344                     .thenReturn(mMaxHingeAngle);
1345             when(mMockRes.getBoolean(com.android.internal.R.bool.config_supportAutoRotation))
1346                     .thenReturn(mSupportAutoRotation);
1347             when(mMockRes.getInteger(com.android.internal.R.integer.config_lidOpenRotation))
1348                     .thenReturn(convertRotationToDegrees(mLidOpenRotation));
1349             when(mMockRes.getInteger(com.android.internal.R.integer.config_carDockRotation))
1350                     .thenReturn(convertRotationToDegrees(mCarDockRotation));
1351             when(mMockRes.getInteger(com.android.internal.R.integer.config_deskDockRotation))
1352                     .thenReturn(convertRotationToDegrees(mDeskDockRotation));
1353             when(mMockRes.getInteger(com.android.internal.R.integer.config_undockedHdmiRotation))
1354                     .thenReturn(convertRotationToDegrees(mUndockedHdmiRotation));
1355 
1356             mMockSensorManager = mock(SensorManager.class);
1357             when(mMockContext.getSystemService(SensorManager.class))
1358                     .thenReturn(mMockSensorManager);
1359             when(mMockContext.getSystemService(Context.SENSOR_SERVICE))
1360                     .thenReturn(mMockSensorManager);
1361             mFakeOrientationSensor = createSensor(Sensor.TYPE_DEVICE_ORIENTATION);
1362             when(mMockSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION)).thenReturn(
1363                     Collections.singletonList(mFakeOrientationSensor));
1364             mFakeHingeAngleSensor = mock(Sensor.class);
1365             when(mMockSensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)).thenReturn(
1366                     mFakeHingeAngleSensor);
1367 
1368             when(mMockContext.getResources().getBoolean(
1369                     com.android.internal.R.bool.config_windowManagerHalfFoldAutoRotateOverride))
1370                     .thenReturn(mSupportHalfFoldAutoRotateOverride);
1371             mMockDisplayRotationReversionController =
1372                     mock(DisplayRotationReversionController.class);
1373             when(mMockDisplayContent.getRotationReversionController())
1374                         .thenReturn(mMockDisplayRotationReversionController);
1375 
1376             mMockResolver = mock(ContentResolver.class);
1377             when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
1378             mFakeSettingsProvider = new FakeSettingsProvider();
1379             when(mMockResolver.acquireProvider(Settings.AUTHORITY))
1380                     .thenReturn(mFakeSettingsProvider.getIContentProvider());
1381 
1382             mMockDisplayAddress = mock(DisplayAddress.class);
1383 
1384             mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
1385 
1386             mMockDeviceStateManager = mock(DeviceStateManager.class);
1387             when(mMockContext.getSystemService(eq(DeviceStateManager.class)))
1388                     .thenReturn(mMockDeviceStateManager);
1389 
1390             mDeviceStateController = mock(DeviceStateController.class);
1391             mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayAddress,
1392                     mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext, new Object(),
1393                     mDeviceStateController) {
1394                 @Override
1395                 DisplayRotationImmersiveAppCompatPolicy initImmersiveAppCompatPolicy(
1396                         WindowManagerService service, DisplayContent displayContent) {
1397                     return mDisplayRotationImmersiveAppCompatPolicyMock;
1398                 }
1399 
1400                 @Override
1401                 Handler getHandler() {
1402                     return sHandler;
1403                 }
1404 
1405                 @Override
1406                 long uptimeMillis() {
1407                     return sCurrentUptimeMillis;
1408                 }
1409             };
1410 
1411             reset(sMockWm);
1412 
1413             verify(mMockSensorManager, atLeast(0)).registerListener(
1414                     mHingeAngleSensorListenerCaptor.capture(), eq(mFakeHingeAngleSensor), anyInt(),
1415                     any());
1416 
1417             captureObservers();
1418         }
1419     }
1420 }
1421