1 /*
2  * Copyright (C) 2022 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.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
20 import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
21 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
22 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
24 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
25 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
26 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
27 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
28 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
29 import static android.view.Surface.ROTATION_0;
30 import static android.view.Surface.ROTATION_90;
31 
32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
35 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
36 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
37 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
38 
39 import static org.junit.Assert.assertEquals;
40 import static org.junit.Assert.assertTrue;
41 import static org.mockito.ArgumentMatchers.any;
42 import static org.mockito.ArgumentMatchers.anyBoolean;
43 import static org.mockito.ArgumentMatchers.anyInt;
44 import static org.mockito.ArgumentMatchers.anyLong;
45 import static org.mockito.ArgumentMatchers.anyString;
46 import static org.mockito.ArgumentMatchers.eq;
47 import static org.mockito.Mockito.mock;
48 import static org.mockito.Mockito.times;
49 import static org.mockito.Mockito.verify;
50 
51 import android.app.servertransaction.ClientTransaction;
52 import android.app.servertransaction.RefreshCallbackItem;
53 import android.app.servertransaction.ResumeActivityItem;
54 import android.content.ComponentName;
55 import android.content.pm.ActivityInfo.ScreenOrientation;
56 import android.content.pm.ApplicationInfo;
57 import android.content.pm.PackageManager;
58 import android.content.res.Configuration;
59 import android.content.res.Configuration.Orientation;
60 import android.hardware.camera2.CameraManager;
61 import android.os.Handler;
62 import android.platform.test.annotations.Presubmit;
63 import android.view.Display;
64 import android.view.Surface.Rotation;
65 
66 import androidx.test.filters.SmallTest;
67 
68 import com.android.internal.R;
69 
70 import org.junit.Before;
71 import org.junit.Test;
72 import org.junit.runner.RunWith;
73 
74 import java.util.concurrent.Executor;
75 
76 /**
77  * Tests for {@link DisplayRotationCompatPolicy}.
78  *
79  * Build/Install/Run:
80  *  atest WmTests:DisplayRotationCompatPolicyTests
81  */
82 @SmallTest
83 @Presubmit
84 @RunWith(WindowTestRunner.class)
85 public final class DisplayRotationCompatPolicyTests extends WindowTestsBase {
86 
87     private static final String TEST_PACKAGE_1 = "com.test.package.one";
88     private static final String TEST_PACKAGE_2 = "com.test.package.two";
89     private static final String CAMERA_ID_1 = "camera-1";
90     private static final String CAMERA_ID_2 = "camera-2";
91     private static final String TEST_PACKAGE_1_LABEL = "testPackage1";
92     private CameraManager mMockCameraManager;
93     private Handler mMockHandler;
94     private LetterboxConfiguration mLetterboxConfiguration;
95 
96     private DisplayRotationCompatPolicy mDisplayRotationCompatPolicy;
97     private CameraManager.AvailabilityCallback mCameraAvailabilityCallback;
98 
99     private ActivityRecord mActivity;
100     private Task mTask;
101 
102     @Before
setUp()103     public void setUp() throws Exception {
104         mLetterboxConfiguration = mDisplayContent.mWmService.mLetterboxConfiguration;
105         spyOn(mLetterboxConfiguration);
106         when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled())
107                 .thenReturn(true);
108         when(mLetterboxConfiguration.isCameraCompatRefreshEnabled())
109                 .thenReturn(true);
110         when(mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
111                 .thenReturn(true);
112 
113         mMockCameraManager = mock(CameraManager.class);
114         doAnswer(invocation -> {
115             mCameraAvailabilityCallback = invocation.getArgument(1);
116             return null;
117         }).when(mMockCameraManager).registerAvailabilityCallback(
118                 any(Executor.class), any(CameraManager.AvailabilityCallback.class));
119 
120         spyOn(mContext);
121         when(mContext.getSystemService(CameraManager.class)).thenReturn(mMockCameraManager);
122 
123         spyOn(mDisplayContent);
124 
125         mDisplayContent.setIgnoreOrientationRequest(true);
126 
127         mMockHandler = mock(Handler.class);
128 
129         when(mMockHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(
130                 invocation -> {
131                     ((Runnable) invocation.getArgument(0)).run();
132                     return null;
133                 });
134         mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy(
135                 mDisplayContent, mMockHandler);
136     }
137 
138     @Test
testOpenedCameraInSplitScreen_showToast()139     public void testOpenedCameraInSplitScreen_showToast() throws Exception {
140         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
141         spyOn(mTask);
142         spyOn(mDisplayRotationCompatPolicy);
143         doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode();
144         doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mTask).getWindowingMode();
145 
146         final PackageManager mockPackageManager = mock(PackageManager.class);
147         final ApplicationInfo mockApplicationInfo = mock(ApplicationInfo.class);
148         when(mContext.getPackageManager()).thenReturn(mockPackageManager);
149         when(mockPackageManager.getApplicationInfo(anyString(), anyInt()))
150                 .thenReturn(mockApplicationInfo);
151 
152         doReturn(TEST_PACKAGE_1_LABEL).when(mockPackageManager)
153                 .getApplicationLabel(mockApplicationInfo);
154 
155         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
156 
157         verify(mDisplayRotationCompatPolicy).showToast(
158                 R.string.display_rotation_camera_compat_toast_in_multi_window,
159                 TEST_PACKAGE_1_LABEL);
160     }
161 
162     @Test
testOpenedCameraInSplitScreen_orientationNotFixed_doNotShowToast()163     public void testOpenedCameraInSplitScreen_orientationNotFixed_doNotShowToast() {
164         configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
165         spyOn(mTask);
166         spyOn(mDisplayRotationCompatPolicy);
167         doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode();
168         doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mTask).getWindowingMode();
169 
170         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
171 
172         verify(mDisplayRotationCompatPolicy, never()).showToast(
173                 R.string.display_rotation_camera_compat_toast_in_multi_window,
174                 TEST_PACKAGE_1_LABEL);
175     }
176 
177     @Test
testOnScreenRotationAnimationFinished_treatmentNotEnabled_doNotShowToast()178     public void testOnScreenRotationAnimationFinished_treatmentNotEnabled_doNotShowToast() {
179         when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled())
180                 .thenReturn(false);
181         spyOn(mDisplayRotationCompatPolicy);
182 
183         mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
184 
185         verify(mDisplayRotationCompatPolicy, never()).showToast(
186                 R.string.display_rotation_camera_compat_toast_after_rotation);
187     }
188 
189     @Test
testOnScreenRotationAnimationFinished_noOpenCamera_doNotShowToast()190     public void testOnScreenRotationAnimationFinished_noOpenCamera_doNotShowToast() {
191         spyOn(mDisplayRotationCompatPolicy);
192 
193         mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
194 
195         verify(mDisplayRotationCompatPolicy, never()).showToast(
196                 R.string.display_rotation_camera_compat_toast_after_rotation);
197     }
198 
199     @Test
testOnScreenRotationAnimationFinished_notFullscreen_doNotShowToast()200     public void testOnScreenRotationAnimationFinished_notFullscreen_doNotShowToast() {
201         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
202         doReturn(true).when(mActivity).inMultiWindowMode();
203         spyOn(mDisplayRotationCompatPolicy);
204 
205         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
206 
207         mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
208 
209         verify(mDisplayRotationCompatPolicy, never()).showToast(
210                 R.string.display_rotation_camera_compat_toast_after_rotation);
211     }
212 
213     @Test
testOnScreenRotationAnimationFinished_orientationNotFixed_doNotShowToast()214     public void testOnScreenRotationAnimationFinished_orientationNotFixed_doNotShowToast() {
215         configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
216         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
217         spyOn(mDisplayRotationCompatPolicy);
218 
219         mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
220 
221         verify(mDisplayRotationCompatPolicy, never()).showToast(
222                 R.string.display_rotation_camera_compat_toast_after_rotation);
223     }
224 
225     @Test
testOnScreenRotationAnimationFinished_showToast()226     public void testOnScreenRotationAnimationFinished_showToast() {
227         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
228         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
229         spyOn(mDisplayRotationCompatPolicy);
230 
231         mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
232 
233         verify(mDisplayRotationCompatPolicy).showToast(
234                 R.string.display_rotation_camera_compat_toast_after_rotation);
235     }
236 
237     @Test
testTreatmentNotEnabled_noForceRotationOrRefresh()238     public void testTreatmentNotEnabled_noForceRotationOrRefresh() throws Exception {
239         when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled())
240                 .thenReturn(false);
241 
242         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
243         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
244 
245         assertEquals(mDisplayRotationCompatPolicy.getOrientation(),
246                 SCREEN_ORIENTATION_UNSPECIFIED);
247 
248         assertNoForceRotationOrRefresh();
249     }
250 
251     @Test
testTreatmentDisabledViaDeviceConfig_noForceRotationOrRefresh()252     public void testTreatmentDisabledViaDeviceConfig_noForceRotationOrRefresh() throws Exception {
253         when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled())
254                 .thenReturn(false);
255 
256         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
257         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
258 
259         assertNoForceRotationOrRefresh();
260     }
261 
262     @Test
testTreatmentDisabledPerApp_noForceRotationOrRefresh()263     public void testTreatmentDisabledPerApp_noForceRotationOrRefresh()
264             throws Exception {
265         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
266         when(mActivity.mLetterboxUiController.shouldForceRotateForCameraCompat())
267                 .thenReturn(false);
268 
269         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
270 
271         assertNoForceRotationOrRefresh();
272     }
273 
274     @Test
testMultiWindowMode_returnUnspecified_noForceRotationOrRefresh()275     public void testMultiWindowMode_returnUnspecified_noForceRotationOrRefresh() throws Exception {
276         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
277         final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, mDisplayContent);
278         mActivity.getTask().reparent(organizer.mPrimary, WindowContainer.POSITION_TOP,
279                 false /* moveParents */, "test" /* reason */);
280 
281         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
282 
283         assertTrue(mActivity.inMultiWindowMode());
284         assertNoForceRotationOrRefresh();
285     }
286 
287     @Test
testOrientationUnspecified_noForceRotationOrRefresh()288     public void testOrientationUnspecified_noForceRotationOrRefresh() throws Exception {
289         configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
290 
291         assertNoForceRotationOrRefresh();
292     }
293 
294     @Test
testOrientationLocked_noForceRotationOrRefresh()295     public void testOrientationLocked_noForceRotationOrRefresh() throws Exception {
296         configureActivity(SCREEN_ORIENTATION_LOCKED);
297 
298         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
299 
300         assertNoForceRotationOrRefresh();
301     }
302 
303     @Test
testOrientationNoSensor_noForceRotationOrRefresh()304     public void testOrientationNoSensor_noForceRotationOrRefresh() throws Exception {
305         configureActivity(SCREEN_ORIENTATION_NOSENSOR);
306 
307         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
308 
309         assertNoForceRotationOrRefresh();
310     }
311 
312     @Test
testIgnoreOrientationRequestIsFalse_noForceRotationOrRefresh()313     public void testIgnoreOrientationRequestIsFalse_noForceRotationOrRefresh() throws Exception {
314         mDisplayContent.setIgnoreOrientationRequest(false);
315 
316         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
317         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
318 
319         assertNoForceRotationOrRefresh();
320     }
321 
322     @Test
testDisplayNotInternal_noForceRotationOrRefresh()323     public void testDisplayNotInternal_noForceRotationOrRefresh() throws Exception {
324         Display display = mDisplayContent.getDisplay();
325         spyOn(display);
326 
327         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
328         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
329 
330         when(display.getType()).thenReturn(Display.TYPE_EXTERNAL);
331         assertNoForceRotationOrRefresh();
332 
333         when(display.getType()).thenReturn(Display.TYPE_WIFI);
334         assertNoForceRotationOrRefresh();
335 
336         when(display.getType()).thenReturn(Display.TYPE_OVERLAY);
337         assertNoForceRotationOrRefresh();
338 
339         when(display.getType()).thenReturn(Display.TYPE_VIRTUAL);
340         assertNoForceRotationOrRefresh();
341     }
342 
343     @Test
testNoCameraConnection_noForceRotationOrRefresh()344     public void testNoCameraConnection_noForceRotationOrRefresh() throws Exception {
345         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
346 
347         assertNoForceRotationOrRefresh();
348     }
349 
350     @Test
testCameraReconnected_forceRotationAndRefresh()351     public void testCameraReconnected_forceRotationAndRefresh() throws Exception {
352         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
353 
354         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
355         mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
356         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
357         callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
358 
359         assertEquals(mDisplayRotationCompatPolicy.getOrientation(),
360                 SCREEN_ORIENTATION_PORTRAIT);
361         assertActivityRefreshRequested(/* refreshRequested */ true);
362     }
363 
364     @Test
testReconnectedToDifferentCamera_forceRotationAndRefresh()365     public void testReconnectedToDifferentCamera_forceRotationAndRefresh() throws Exception {
366         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
367 
368         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
369         mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
370         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_2, TEST_PACKAGE_1);
371         callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
372 
373         assertEquals(mDisplayRotationCompatPolicy.getOrientation(),
374                 SCREEN_ORIENTATION_PORTRAIT);
375         assertActivityRefreshRequested(/* refreshRequested */ true);
376     }
377 
378     @Test
testCameraDisconnected_revertRotationAndRefresh()379     public void testCameraDisconnected_revertRotationAndRefresh() throws Exception {
380         configureActivityAndDisplay(SCREEN_ORIENTATION_PORTRAIT, ORIENTATION_LANDSCAPE);
381         // Open camera and test for compat treatment
382         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
383         callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
384         assertEquals(mDisplayRotationCompatPolicy.getOrientation(),
385                 SCREEN_ORIENTATION_LANDSCAPE);
386         assertActivityRefreshRequested(/* refreshRequested */ true);
387         // Close camera and test for revert
388         mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
389         callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
390         assertEquals(mDisplayRotationCompatPolicy.getOrientation(),
391                 SCREEN_ORIENTATION_UNSPECIFIED);
392         assertActivityRefreshRequested(/* refreshRequested */ true);
393     }
394 
395     @Test
testGetOrientation_cameraConnectionClosed_returnUnspecified()396     public void testGetOrientation_cameraConnectionClosed_returnUnspecified() {
397         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
398 
399         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
400 
401         assertEquals(mDisplayRotationCompatPolicy.getOrientation(),
402                 SCREEN_ORIENTATION_PORTRAIT);
403 
404         mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
405 
406         assertEquals(mDisplayRotationCompatPolicy.getOrientation(),
407                 SCREEN_ORIENTATION_UNSPECIFIED);
408     }
409 
410     @Test
testCameraOpenedForDifferentPackage_noForceRotationOrRefresh()411     public void testCameraOpenedForDifferentPackage_noForceRotationOrRefresh() throws Exception {
412         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
413 
414         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_2);
415 
416         assertNoForceRotationOrRefresh();
417     }
418 
419     @Test
testGetOrientation_portraitActivity_portraitNaturalOrientation_returnPortrait()420     public void testGetOrientation_portraitActivity_portraitNaturalOrientation_returnPortrait() {
421         testGetOrientationForActivityAndNaturalOrientations(
422                 /* activityOrientation */ SCREEN_ORIENTATION_PORTRAIT,
423                 /* naturalOrientation */ ORIENTATION_PORTRAIT,
424                 /* expectedOrientation */ SCREEN_ORIENTATION_PORTRAIT);
425     }
426 
427     @Test
testGetOrientation_portraitActivity_landscapeNaturalOrientation_returnLandscape()428     public void testGetOrientation_portraitActivity_landscapeNaturalOrientation_returnLandscape() {
429         testGetOrientationForActivityAndNaturalOrientations(
430                 /* activityOrientation */ SCREEN_ORIENTATION_PORTRAIT,
431                 /* naturalOrientation */ ORIENTATION_LANDSCAPE,
432                 /* expectedOrientation */ SCREEN_ORIENTATION_LANDSCAPE);
433     }
434 
435     @Test
testGetOrientation_landscapeActivity_portraitNaturalOrientation_returnLandscape()436     public void testGetOrientation_landscapeActivity_portraitNaturalOrientation_returnLandscape() {
437         testGetOrientationForActivityAndNaturalOrientations(
438                 /* activityOrientation */ SCREEN_ORIENTATION_LANDSCAPE,
439                 /* naturalOrientation */ ORIENTATION_PORTRAIT,
440                 /* expectedOrientation */ SCREEN_ORIENTATION_LANDSCAPE);
441     }
442 
443     @Test
testGetOrientation_landscapeActivity_landscapeNaturalOrientation_returnPortrait()444     public void testGetOrientation_landscapeActivity_landscapeNaturalOrientation_returnPortrait() {
445         testGetOrientationForActivityAndNaturalOrientations(
446                 /* activityOrientation */ SCREEN_ORIENTATION_LANDSCAPE,
447                 /* naturalOrientation */ ORIENTATION_LANDSCAPE,
448                 /* expectedOrientation */ SCREEN_ORIENTATION_PORTRAIT);
449     }
450 
testGetOrientationForActivityAndNaturalOrientations( @creenOrientation int activityOrientation, @Orientation int naturalOrientation, @ScreenOrientation int expectedOrientation)451     private void testGetOrientationForActivityAndNaturalOrientations(
452             @ScreenOrientation int activityOrientation,
453             @Orientation int naturalOrientation,
454             @ScreenOrientation int expectedOrientation) {
455         configureActivityAndDisplay(activityOrientation, naturalOrientation);
456 
457         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
458 
459         assertEquals(mDisplayRotationCompatPolicy.getOrientation(),
460                 expectedOrientation);
461     }
462 
463     @Test
testOnActivityConfigurationChanging_refreshDisabledViaFlag_noRefresh()464     public void testOnActivityConfigurationChanging_refreshDisabledViaFlag_noRefresh()
465             throws Exception {
466         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
467         when(mActivity.mLetterboxUiController.shouldRefreshActivityForCameraCompat())
468                 .thenReturn(false);
469 
470         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
471         callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
472 
473         assertActivityRefreshRequested(/* refreshRequested */ false);
474     }
475 
476     @Test
testOnActivityConfigurationChanging_refreshDisabledPerApp_noRefresh()477     public void testOnActivityConfigurationChanging_refreshDisabledPerApp_noRefresh()
478             throws Exception {
479         when(mLetterboxConfiguration.isCameraCompatRefreshEnabled()).thenReturn(false);
480 
481         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
482 
483         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
484         callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
485 
486         assertActivityRefreshRequested(/* refreshRequested */ false);
487     }
488 
489     @Test
testOnActivityConfigurationChanging_displayRotationNotChanging_noRefresh()490     public void testOnActivityConfigurationChanging_displayRotationNotChanging_noRefresh()
491             throws Exception {
492         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
493         doReturn(false).when(mActivity.mLetterboxUiController)
494                 .isCameraCompatSplitScreenAspectRatioAllowed();
495 
496         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
497         callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ false);
498 
499         assertActivityRefreshRequested(/* refreshRequested */ false);
500     }
501 
502     @Test
testOnActivityConfigurationChanging_splitScreenAspectRatioAllowed_refresh()503     public void testOnActivityConfigurationChanging_splitScreenAspectRatioAllowed_refresh()
504             throws Exception {
505         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
506         doReturn(true).when(mActivity.mLetterboxUiController)
507                 .isCameraCompatSplitScreenAspectRatioAllowed();
508 
509         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
510         callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ false);
511 
512         assertActivityRefreshRequested(/* refreshRequested */ true);
513     }
514 
515     @Test
testOnActivityConfigurationChanging_cycleThroughStopDisabled()516     public void testOnActivityConfigurationChanging_cycleThroughStopDisabled() throws Exception {
517         when(mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
518                 .thenReturn(false);
519 
520         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
521 
522         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
523         callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
524 
525         assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false);
526     }
527 
528     @Test
testOnActivityConfigurationChanging_cycleThroughStopDisabledForApp()529     public void testOnActivityConfigurationChanging_cycleThroughStopDisabledForApp()
530             throws Exception {
531         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
532         when(mActivity.mLetterboxUiController.shouldRefreshActivityViaPauseForCameraCompat())
533                 .thenReturn(true);
534 
535         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
536         callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
537 
538         assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false);
539     }
540 
configureActivity(@creenOrientation int activityOrientation)541     private void configureActivity(@ScreenOrientation int activityOrientation) {
542         configureActivityAndDisplay(activityOrientation, ORIENTATION_PORTRAIT);
543     }
544 
configureActivityAndDisplay(@creenOrientation int activityOrientation, @Orientation int naturalOrientation)545     private void configureActivityAndDisplay(@ScreenOrientation int activityOrientation,
546             @Orientation int naturalOrientation) {
547 
548         mTask = new TaskBuilder(mSupervisor)
549                 .setDisplay(mDisplayContent)
550                 .build();
551 
552         mActivity = new ActivityBuilder(mAtm)
553                 .setComponent(new ComponentName(TEST_PACKAGE_1, ".TestActivity"))
554                 .setScreenOrientation(activityOrientation)
555                 .setTask(mTask)
556                 .build();
557 
558         spyOn(mActivity.mAtmService.getLifecycleManager());
559         spyOn(mActivity.mLetterboxUiController);
560 
561         doReturn(mActivity).when(mDisplayContent).topRunningActivity(anyBoolean());
562         doReturn(naturalOrientation).when(mDisplayContent).getNaturalOrientation();
563     }
564 
assertActivityRefreshRequested(boolean refreshRequested)565     private void assertActivityRefreshRequested(boolean refreshRequested) throws Exception {
566         assertActivityRefreshRequested(refreshRequested, /* cycleThroughStop*/ true);
567     }
568 
assertActivityRefreshRequested(boolean refreshRequested, boolean cycleThroughStop)569     private void assertActivityRefreshRequested(boolean refreshRequested,
570                 boolean cycleThroughStop) throws Exception {
571         verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0))
572                 .setIsRefreshAfterRotationRequested(true);
573 
574         final ClientTransaction transaction = ClientTransaction.obtain(
575                 mActivity.app.getThread(), mActivity.token);
576         transaction.addCallback(RefreshCallbackItem.obtain(cycleThroughStop ? ON_STOP : ON_PAUSE));
577         transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(
578                 /* isForward */ false, /* shouldSendCompatFakeFocus */ false));
579 
580         verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0))
581                 .scheduleTransaction(eq(transaction));
582     }
583 
assertNoForceRotationOrRefresh()584     private void assertNoForceRotationOrRefresh() throws Exception {
585         callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
586 
587         assertEquals(mDisplayRotationCompatPolicy.getOrientation(),
588                 SCREEN_ORIENTATION_UNSPECIFIED);
589         assertActivityRefreshRequested(/* refreshRequested */ false);
590     }
591 
callOnActivityConfigurationChanging( ActivityRecord activity, boolean isDisplayRotationChanging)592     private void callOnActivityConfigurationChanging(
593             ActivityRecord activity, boolean isDisplayRotationChanging) {
594         mDisplayRotationCompatPolicy.onActivityConfigurationChanging(activity,
595                 /* newConfig */ createConfigurationWithDisplayRotation(ROTATION_0),
596                 /* newConfig */ createConfigurationWithDisplayRotation(
597                         isDisplayRotationChanging ? ROTATION_90 : ROTATION_0));
598     }
599 
createConfigurationWithDisplayRotation(@otation int rotation)600     private static Configuration createConfigurationWithDisplayRotation(@Rotation int rotation) {
601         final Configuration config = new Configuration();
602         config.windowConfiguration.setDisplayRotation(rotation);
603         return config;
604     }
605 }
606