1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package android.server.wm;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
24 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
25 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
26 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
27 import static android.server.wm.StateLogger.logE;
28 import static android.server.wm.WindowManagerState.STATE_RESUMED;
29 import static android.server.wm.WindowManagerState.dpToPx;
30 import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
31 import static android.server.wm.app.Components.DIALOG_WHEN_LARGE_ACTIVITY;
32 import static android.server.wm.app.Components.LANDSCAPE_ORIENTATION_ACTIVITY;
33 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
34 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_APP_CONFIG_INFO;
35 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_CONFIG_INFO_IN_ON_CREATE;
36 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_DISPLAY_REAL_SIZE;
37 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_SYSTEM_RESOURCES_CONFIG_INFO;
38 import static android.server.wm.app.Components.NIGHT_MODE_ACTIVITY;
39 import static android.server.wm.app.Components.PORTRAIT_ORIENTATION_ACTIVITY;
40 import static android.server.wm.app.Components.RESIZEABLE_ACTIVITY;
41 import static android.server.wm.app.Components.TEST_ACTIVITY;
42 import static android.server.wm.translucentapp26.Components.SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY;
43 import static android.view.Surface.ROTATION_0;
44 import static android.view.Surface.ROTATION_180;
45 import static android.view.Surface.ROTATION_270;
46 import static android.view.Surface.ROTATION_90;
47 
48 import static org.hamcrest.MatcherAssert.assertThat;
49 import static org.hamcrest.Matchers.lessThan;
50 import static org.junit.Assert.assertEquals;
51 import static org.junit.Assert.assertTrue;
52 import static org.junit.Assert.assertFalse;
53 import static org.junit.Assert.assertNotEquals;
54 import static org.junit.Assert.fail;
55 import static org.junit.Assume.assumeFalse;
56 import static org.junit.Assume.assumeTrue;
57 
58 import android.app.Activity;
59 import android.content.ComponentName;
60 import android.content.Context;
61 import android.content.res.Resources;
62 import android.graphics.Point;
63 import android.graphics.Rect;
64 import android.hardware.display.DisplayManager;
65 import android.os.Bundle;
66 import android.platform.test.annotations.Presubmit;
67 import android.server.wm.CommandSession.ActivitySession;
68 import android.server.wm.CommandSession.ActivitySessionClient;
69 import android.server.wm.CommandSession.ConfigInfo;
70 import android.server.wm.CommandSession.SizeInfo;
71 import android.server.wm.TestJournalProvider.TestJournalContainer;
72 import android.util.DisplayMetrics;
73 import android.view.Display;
74 
75 import org.junit.Test;
76 
77 import java.util.function.Function;
78 
79 /**
80  * Build/Install/Run:
81  *     atest CtsWindowManagerDeviceTestCases:AppConfigurationTests
82  */
83 @Presubmit
84 public class AppConfigurationTests extends MultiDisplayTestBase {
85 
86     private static final int SMALL_WIDTH_DP = 426;
87     private static final int SMALL_HEIGHT_DP = 320;
88 
89     /**
90      * Tests that the WindowManager#getDefaultDisplay() and the Configuration of the Activity
91      * has an updated size when the Activity is resized from fullscreen to docked state.
92      *
93      * The Activity handles configuration changes, so it will not be restarted between resizes.
94      * On Configuration changes, the Activity logs the Display size and Configuration width
95      * and heights. The values reported in fullscreen should be larger than those reported in
96      * docked state.
97      */
98     @Test
testConfigurationUpdatesWhenResizedFromFullscreen()99     public void testConfigurationUpdatesWhenResizedFromFullscreen() {
100         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
101 
102         separateTestJournal();
103         launchActivity(RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
104         final SizeInfo fullscreenSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
105 
106         separateTestJournal();
107         putActivityInPrimarySplit(RESIZEABLE_ACTIVITY);
108         final SizeInfo dockedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
109 
110         assertSizesAreSane(fullscreenSizes, dockedSizes);
111     }
112 
113     /**
114      * Same as {@link #testConfigurationUpdatesWhenResizedFromFullscreen()} but resizing
115      * from docked state to fullscreen (reverse).
116      */
117     @Test
testConfigurationUpdatesWhenResizedFromDockedStack()118     public void testConfigurationUpdatesWhenResizedFromDockedStack() {
119         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
120 
121         separateTestJournal();
122         launchActivitiesInSplitScreen(
123                 getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY)
124                                         .setWindowingMode(WINDOWING_MODE_FULLSCREEN),
125                 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
126                                         .setWindowingMode(WINDOWING_MODE_FULLSCREEN));
127 
128         final SizeInfo dockedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
129 
130         separateTestJournal();
131         dismissSplitScreen(true /* primaryOnTop */);
132         final SizeInfo fullscreenSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
133 
134         assertSizesAreSane(fullscreenSizes, dockedSizes);
135     }
136 
137     /**
138      * Tests whether the Display sizes change when rotating the device.
139      */
140     @Test
testConfigurationUpdatesWhenRotatingWhileFullscreen()141     public void testConfigurationUpdatesWhenRotatingWhileFullscreen() {
142         assumeTrue("Skipping test: no rotation support", supportsRotation());
143 
144         final RotationSession rotationSession = createManagedRotationSession();
145         rotationSession.set(ROTATION_0);
146 
147         separateTestJournal();
148         final ActivitySessionClient resizeableActivityClient = createManagedActivityClientSession();
149         resizeableActivityClient.startActivity(getLaunchActivityBuilder()
150                         .setUseInstrumentation()
151                         .setTargetActivity(RESIZEABLE_ACTIVITY)
152                         .setWindowingMode(WINDOWING_MODE_FULLSCREEN));
153         final SizeInfo initialSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
154 
155         rotateAndCheckSizes(rotationSession, resizeableActivityClient, initialSizes);
156     }
157 
158     /**
159      * Same as {@link #testConfigurationUpdatesWhenRotatingWhileFullscreen()} but when the Activity
160      * is in the docked stack.
161      */
162     @Test
testConfigurationUpdatesWhenRotatingWhileDocked()163     public void testConfigurationUpdatesWhenRotatingWhileDocked() {
164         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
165 
166         final ActivitySessionClient resizeableActivityClient = createManagedActivityClientSession();
167         final RotationSession rotationSession = createManagedRotationSession();
168         rotationSession.set(ROTATION_0);
169 
170         separateTestJournal();
171         // Launch our own activity to side in case Recents (or other activity to side) doesn't
172         // support rotation.
173         launchActivitiesInSplitScreen(
174                 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
175                 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
176         // Launch target activity in docked stack.
177         getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY)
178                 .setActivitySessionClient(resizeableActivityClient).execute();
179         final SizeInfo initialSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
180 
181         rotateAndCheckSizes(rotationSession, resizeableActivityClient, initialSizes);
182     }
183 
184     /**
185      * Same as {@link #testConfigurationUpdatesWhenRotatingWhileDocked()} but when the Activity
186      * is launched to side from docked stack.
187      */
188     @Test
testConfigurationUpdatesWhenRotatingToSideFromDocked()189     public void testConfigurationUpdatesWhenRotatingToSideFromDocked() {
190         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
191 
192         final ActivitySessionClient resizeableActivityClient = createManagedActivityClientSession();
193         final RotationSession rotationSession = createManagedRotationSession();
194         rotationSession.set(ROTATION_0);
195 
196         separateTestJournal();
197         launchActivitiesInSplitScreen(
198                 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
199                 getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY)
200                         .setActivitySessionClient(resizeableActivityClient));
201         final SizeInfo initialSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
202 
203         rotateAndCheckSizes(rotationSession, resizeableActivityClient, initialSizes);
204     }
205 
rotateAndCheckSizes(RotationSession rotationSession, ActivitySessionClient noRelaunchActivityClient, SizeInfo prevSizes)206     private void rotateAndCheckSizes(RotationSession rotationSession,
207             ActivitySessionClient noRelaunchActivityClient, SizeInfo prevSizes) {
208         final ActivitySession activitySession = noRelaunchActivityClient.getLastStartedSession();
209         final ComponentName activityName = activitySession.getName();
210         final WindowManagerState.Task task = mWmState.getTaskByActivity(activityName);
211         final int displayId = mWmState.getRootTask(task.mRootTaskId).mDisplayId;
212 
213         assumeTrue(supportsLockedUserRotation(rotationSession, displayId));
214 
215         final boolean isCloseToSquareDisplay = isCloseToSquareDisplay();
216         final int[] rotations = { ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0 };
217         for (final int rotation : rotations) {
218             separateTestJournal();
219             rotationSession.set(rotation);
220             final int newDeviceRotation = getDeviceRotation(displayId);
221             if (newDeviceRotation == INVALID_DEVICE_ROTATION) {
222                 logE("Got an invalid device rotation value. "
223                         + "Continuing the test despite of that, but it is likely to fail.");
224             }
225             final boolean expectConfigChange = task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
226                     && !isCloseToSquareDisplay;
227             if (expectConfigChange) {
228                 assertActivityLifecycle(activityName, false /* relaunch */);
229             }
230             final SizeInfo rotatedSizes = activitySession.getConfigInfo().sizeInfo;
231             assertSizesRotate(prevSizes, rotatedSizes,
232                     // Skip orientation checks if we are not in fullscreen mode, or when the display
233                     // is close to square because the app config orientation may always be landscape
234                     // excluding the system insets.
235                     !expectConfigChange /* skipOrientationCheck */);
236             prevSizes = rotatedSizes;
237         }
238     }
239 
240     /**
241      * Tests when activity moved from fullscreen stack to docked and back. Activity will be
242      * relaunched twice and it should have same config as initial one.
243      */
244     @Test
testSameConfigurationFullSplitFullRelaunch()245     public void testSameConfigurationFullSplitFullRelaunch() {
246         moveActivityFullSplitFull(true /* relaunch */);
247     }
248 
249     /**
250      * Same as {@link #testSameConfigurationFullSplitFullRelaunch} but without relaunch.
251      */
252     @Test
testSameConfigurationFullSplitFullNoRelaunch()253     public void testSameConfigurationFullSplitFullNoRelaunch() {
254         moveActivityFullSplitFull(false /* relaunch */);
255     }
256 
257     /**
258      * Launches activity in fullscreen task, moves to docked task and back to fullscreen task.
259      * Asserts that initial and final reported sizes in fullscreen task are the same.
260      */
moveActivityFullSplitFull(boolean relaunch)261     private void moveActivityFullSplitFull(boolean relaunch) {
262         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
263 
264         final ComponentName activityName = relaunch ? TEST_ACTIVITY : RESIZEABLE_ACTIVITY;
265         // Launch to fullscreen task and record size.
266         separateTestJournal();
267         launchActivity(activityName, WINDOWING_MODE_FULLSCREEN);
268         final SizeInfo initialFullscreenSizes = getLastReportedSizesForActivity(activityName);
269 
270         // Move the task to the primary split task.
271         separateTestJournal();
272         putActivityInPrimarySplit(activityName);
273         // Currently launchActivityInPrimarySplit launches the target activity and then move it
274         // to split task, so it requires waiting of lifecycle to get the stable initial size.
275         if (relaunch) {
276             assertActivityLifecycle(activityName, true /* relaunch */);
277         } else {
278             // The lifecycle callbacks contain the initial launch event so only wait for
279             // multi-window mode changed.
280             waitForOnMultiWindowModeChanged(activityName);
281         }
282         final SizeInfo dockedSizes = getLastReportedSizesForActivity(activityName);
283         assertSizesAreSane(initialFullscreenSizes, dockedSizes);
284 
285         // Restore to fullscreen.
286         separateTestJournal();
287         mTaskOrganizer.dismissSplitScreen();
288         // Home task could be on top since it was the top-most task while in split-screen mode
289         // (dock task was minimized), start the activity again to ensure the activity is at
290         // foreground.
291         launchActivity(activityName, WINDOWING_MODE_FULLSCREEN);
292         assertActivityLifecycle(activityName, relaunch);
293         final SizeInfo finalFullscreenSizes = getLastReportedSizesForActivity(activityName);
294 
295         // After activity configuration was changed twice it must report same size as original one.
296         assertSizesAreSame(initialFullscreenSizes, finalFullscreenSizes);
297     }
298 
299     /**
300      * Tests that an activity with the DialogWhenLarge theme can transform properly when in split
301      * screen.
302      */
303     @Test
testDialogWhenLargeSplitSmall()304     public void testDialogWhenLargeSplitSmall() {
305         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
306 
307         launchActivitiesInSplitScreen(
308                 getLaunchActivityBuilder().setTargetActivity(DIALOG_WHEN_LARGE_ACTIVITY),
309                 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
310         int displayId = mWmState.getDisplayByActivity(DIALOG_WHEN_LARGE_ACTIVITY);
311         final WindowManagerState.DisplayContent display = mWmState.getDisplay(displayId);
312         final int density = display.getDpi();
313         final int smallWidthPx = dpToPx(SMALL_WIDTH_DP, density);
314         final int smallHeightPx = dpToPx(SMALL_HEIGHT_DP, density);
315 
316         mTaskOrganizer.setRootPrimaryTaskBounds(new Rect(0, 0, smallWidthPx, smallHeightPx));
317         mWmState.waitForValidState(
318                 new WaitForValidActivityState.Builder(DIALOG_WHEN_LARGE_ACTIVITY)
319                         .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
320                         .setActivityType(ACTIVITY_TYPE_STANDARD)
321                         .build());
322     }
323 
324     /**
325      * Test that device handles consequent requested orientations and displays the activities.
326      */
327     @Test
testFullscreenAppOrientationRequests()328     public void testFullscreenAppOrientationRequests() {
329         assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest());
330 
331         separateTestJournal();
332         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
333         mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
334         SizeInfo reportedSizes = getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY);
335         assertEquals("portrait activity should be in portrait",
336                 ORIENTATION_PORTRAIT, reportedSizes.orientation);
337         assertTrue("portrait activity should have height >= width",
338                 reportedSizes.heightDp >= reportedSizes.widthDp);
339         separateTestJournal();
340 
341         launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY);
342         mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */);
343         reportedSizes = getLastReportedSizesForActivity(LANDSCAPE_ORIENTATION_ACTIVITY);
344         assertEquals("landscape activity should be in landscape",
345                 ORIENTATION_LANDSCAPE, reportedSizes.orientation);
346         assertTrue("landscape activity should have height < width",
347                 reportedSizes.heightDp < reportedSizes.widthDp);
348         separateTestJournal();
349 
350         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
351         mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
352         reportedSizes = getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY);
353         assertEquals("portrait activity should be in portrait",
354                 ORIENTATION_PORTRAIT, reportedSizes.orientation);
355     }
356 
357     @Test
testTranslucentAppOrientationRequests()358     public void testTranslucentAppOrientationRequests() {
359         assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest());
360 
361         separateTestJournal();
362         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
363         final SizeInfo initialReportedSizes =
364                 getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY);
365         assertEquals("portrait activity should be in portrait",
366                 ORIENTATION_PORTRAIT, initialReportedSizes.orientation);
367         assertTrue("portrait activity should have height >= width",
368                 initialReportedSizes.heightDp >= initialReportedSizes.widthDp);
369         separateTestJournal();
370 
371         launchActivity(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
372         assertEquals("Legacy translucent activity requested landscape orientation",
373                 SCREEN_ORIENTATION_LANDSCAPE, mWmState.getLastOrientation());
374 
375         // TODO(b/36897968): uncomment once we can suppress unsupported configurations
376         // final ReportedSizes updatedReportedSizes =
377         //      getLastReportedSizesForActivity(PORTRAIT_ACTIVITY_NAME, logSeparator);
378         // assertEquals("portrait activity should not have moved from portrait",
379         //         1 /* portrait */, updatedReportedSizes.orientation);
380     }
381 
382     /**
383      * Test that device handles consequent requested orientations and will not report a config
384      * change to an invisible activity.
385      */
386     @Test
testAppOrientationRequestConfigChanges()387     public void testAppOrientationRequestConfigChanges() {
388         assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest());
389 
390         separateTestJournal();
391         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
392         mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
393 
394         assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY,
395                 1 /* create */, 1 /* start */, 1 /* resume */,
396                 0 /* pause */, 0 /* stop */, 0 /* destroy */, 0 /* config */);
397 
398         launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
399         mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */);
400 
401         assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY,
402                 1 /* create */, 1 /* start */, 1 /* resume */,
403                 1 /* pause */, 1 /* stop */, 0 /* destroy */, 0 /* config */);
404         assertLifecycleCounts(LANDSCAPE_ORIENTATION_ACTIVITY,
405                 1 /* create */, 1 /* start */, 1 /* resume */,
406                 0 /* pause */, 0 /* stop */, 0 /* destroy */, 0 /* config */);
407 
408         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
409         mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
410 
411         assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY,
412                 2 /* create */, 2 /* start */, 2 /* resume */,
413                 1 /* pause */, 1 /* stop */, 0 /* destroy */, 0 /* config */);
414         assertLifecycleCounts(LANDSCAPE_ORIENTATION_ACTIVITY,
415                 1 /* create */, 1 /* start */, 1 /* resume */,
416                 1 /* pause */, 1 /* stop */, 0 /* destroy */, 0 /* config */);
417     }
418 
419     /**
420      * Test that device orientation is restored when an activity that requests it is no longer
421      * visible.
422      *
423      * TODO(b/139936670, b/112688380): This test case fails on some vendor devices which has
424      * rotation sensing optimization. So this is listed in cts-known-failures.xml.
425      */
426     @Test
testAppOrientationRequestConfigClears()427     public void testAppOrientationRequestConfigClears() {
428         assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest());
429 
430         separateTestJournal();
431         launchActivity(TEST_ACTIVITY);
432         mWmState.assertVisibility(TEST_ACTIVITY, true /* visible */);
433         final SizeInfo initialReportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY);
434         final int initialOrientation = initialReportedSizes.orientation;
435 
436         // Launch an activity that requests different orientation and check that it will be applied
437         final boolean launchingPortrait;
438         if (initialOrientation == ORIENTATION_LANDSCAPE) {
439             launchingPortrait = true;
440         } else if (initialOrientation == ORIENTATION_PORTRAIT) {
441             launchingPortrait = false;
442         } else {
443             fail("Unexpected orientation value: " + initialOrientation);
444             return;
445         }
446         final ComponentName differentOrientationActivity = launchingPortrait
447                 ? PORTRAIT_ORIENTATION_ACTIVITY : LANDSCAPE_ORIENTATION_ACTIVITY;
448         separateTestJournal();
449         launchActivity(differentOrientationActivity);
450         mWmState.assertVisibility(differentOrientationActivity, true /* visible */);
451         final SizeInfo rotatedReportedSizes =
452                 getLastReportedSizesForActivity(differentOrientationActivity);
453         assertEquals("Applied orientation must correspond to activity request",
454                 launchingPortrait ? 1 : 2, rotatedReportedSizes.orientation);
455 
456         // Launch another activity on top and check that its orientation is not affected by previous
457         // activity.
458         separateTestJournal();
459         launchActivity(RESIZEABLE_ACTIVITY);
460         mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
461         final SizeInfo finalReportedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
462         assertEquals("Applied orientation must not be influenced by previously visible activity",
463                 initialOrientation, finalReportedSizes.orientation);
464     }
465 
466     @Test
testRotatedInfoWithFixedRotationTransform()467     public void testRotatedInfoWithFixedRotationTransform() {
468         assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest());
469 
470         // Start a portrait activity first to ensure that the orientation will change.
471         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
472         mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
473 
474         getLaunchActivityBuilder()
475                 .setUseInstrumentation()
476                 .setTargetActivity(LANDSCAPE_ORIENTATION_ACTIVITY)
477                 // Request the info from onCreate because at that moment the real display hasn't
478                 // rotated but the activity is rotated.
479                 .setIntentExtra(bundle -> bundle.putBoolean(EXTRA_CONFIG_INFO_IN_ON_CREATE, true))
480                 .execute();
481         mWmState.waitForLastOrientation(SCREEN_ORIENTATION_LANDSCAPE);
482 
483         final SizeInfo reportedSizes =
484                 getLastReportedSizesForActivity(LANDSCAPE_ORIENTATION_ACTIVITY);
485         final Bundle extras = TestJournalContainer.get(LANDSCAPE_ORIENTATION_ACTIVITY).extras;
486         final ConfigInfo appConfigInfo = extras.getParcelable(EXTRA_APP_CONFIG_INFO);
487         final Point onCreateRealDisplaySize = extras.getParcelable(EXTRA_DISPLAY_REAL_SIZE);
488         final ConfigInfo onCreateConfigInfo = extras.getParcelable(EXTRA_CONFIG_INFO_IN_ON_CREATE);
489         final SizeInfo onCreateSize = onCreateConfigInfo.sizeInfo;
490         final ConfigInfo globalConfigInfo =
491                 extras.getParcelable(EXTRA_SYSTEM_RESOURCES_CONFIG_INFO);
492         final SizeInfo globalSizeInfo = globalConfigInfo.sizeInfo;
493 
494         assertEquals("The last reported size should be the same as the one from onCreate",
495                 reportedSizes, onCreateConfigInfo.sizeInfo);
496 
497         final Display display = mDm.getDisplay(Display.DEFAULT_DISPLAY);
498         final Point expectedRealDisplaySize = new Point();
499         display.getRealSize(expectedRealDisplaySize);
500 
501         final int expectedRotation = display.getRotation();
502         assertEquals("The activity should get the final display rotation in onCreate",
503                 expectedRotation, onCreateConfigInfo.rotation);
504         assertEquals("The application should get the final display rotation in onCreate",
505                 expectedRotation, appConfigInfo.rotation);
506         assertEquals("The orientation of application must be landscape",
507                 ORIENTATION_LANDSCAPE, appConfigInfo.sizeInfo.orientation);
508         assertEquals("The orientation of system resources must be landscape",
509                 ORIENTATION_LANDSCAPE, globalSizeInfo.orientation);
510         assertEquals("The activity should get the final display size in onCreate",
511                 expectedRealDisplaySize, onCreateRealDisplaySize);
512 
513         final boolean isLandscape = expectedRealDisplaySize.x > expectedRealDisplaySize.y;
514         assertEquals("The app size of activity should have the same orientation", isLandscape,
515                 onCreateSize.displayWidth > onCreateSize.displayHeight);
516         assertEquals("The application should get the same orientation", isLandscape,
517                 appConfigInfo.sizeInfo.displayWidth > appConfigInfo.sizeInfo.displayHeight);
518         assertEquals("The app display metrics must be landscape", isLandscape,
519                 appConfigInfo.sizeInfo.metricsWidth > appConfigInfo.sizeInfo.metricsHeight);
520 
521         final DisplayMetrics globalMetrics = Resources.getSystem().getDisplayMetrics();
522         assertEquals("The display metrics of system resources must be landscape",
523                 new Point(globalMetrics.widthPixels, globalMetrics.heightPixels),
524                 new Point(globalSizeInfo.metricsWidth, globalSizeInfo.metricsHeight));
525     }
526 
527     @Test
testTranslucentActivityPermitted()528     public void testTranslucentActivityPermitted() throws Exception {
529         assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest());
530 
531         final RotationSession rotationSession = createManagedRotationSession();
532         rotationSession.set(ROTATION_0);
533 
534         launchActivity(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
535         mWmState.assertResumedActivity(
536                 "target SDK <= 26 translucent activity should be allowed to launch",
537                 SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY);
538         assertEquals("translucent activity requested landscape orientation",
539                 SCREEN_ORIENTATION_LANDSCAPE, mWmState.getLastOrientation());
540     }
541 
542     /**
543      * Test that device handles moving between two tasks with different orientations.
544      */
545     @Test
testTaskCloseRestoreFixedOrientation()546     public void testTaskCloseRestoreFixedOrientation() {
547         assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest());
548 
549         // Start landscape activity.
550         launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
551         mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */);
552         mWmState.waitAndAssertLastOrientation("Fullscreen app requested landscape orientation",
553                 SCREEN_ORIENTATION_LANDSCAPE);
554 
555         // Start another activity in a different task.
556         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
557 
558         // Request portrait
559         mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT);
560         mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
561         waitForBroadcastActivityReady(ORIENTATION_PORTRAIT);
562 
563         // Finish activity
564         mBroadcastActionTrigger.finishBroadcastReceiverActivity();
565 
566         // Verify that activity brought to front is in originally requested orientation.
567         mWmState.computeState(LANDSCAPE_ORIENTATION_ACTIVITY);
568         mWmState.waitAndAssertLastOrientation("Should return to app in landscape orientation",
569                 SCREEN_ORIENTATION_LANDSCAPE);
570     }
571 
572     /**
573      * Test that device handles moving between two tasks with different orientations.
574      *
575      * TODO(b/139936670, b/112688380): This test case fails on some vendor devices which has
576      * rotation sensing optimization. So this is listed in cts-known-failures.xml.
577      */
578     @Test
testTaskCloseRestoreFreeOrientation()579     public void testTaskCloseRestoreFreeOrientation() {
580         assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest());
581 
582         // Start landscape activity.
583         launchActivity(RESIZEABLE_ACTIVITY);
584         mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
585         final int initialServerOrientation = mWmState.getLastOrientation();
586 
587         // Verify fixed-landscape
588         separateTestJournal();
589         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
590         mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_LANDSCAPE);
591         mWmState.waitForLastOrientation(SCREEN_ORIENTATION_LANDSCAPE);
592         waitForBroadcastActivityReady(ORIENTATION_LANDSCAPE);
593         mBroadcastActionTrigger.finishBroadcastReceiverActivity();
594 
595         // Verify that activity brought to front is in originally requested orientation.
596         mWmState.waitForActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED);
597         mWmState.waitAndAssertLastOrientation("Should come back in original server orientation",
598                 initialServerOrientation);
599         assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */,
600                 0 /* numConfigChange */);
601 
602         // Verify fixed-portrait
603         separateTestJournal();
604         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
605         mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT);
606         mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
607         waitForBroadcastActivityReady(ORIENTATION_PORTRAIT);
608         mBroadcastActionTrigger.finishBroadcastReceiverActivity();
609 
610         // Verify that activity brought to front is in originally requested orientation.
611         mWmState.waitForActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED);
612         mWmState.waitAndAssertLastOrientation("Should come back in original server orientation",
613                 initialServerOrientation);
614         assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */,
615                 0 /* numConfigChange */);
616     }
617 
618     /**
619      * Test that activity orientation will change when device is rotated.
620      * Also verify that occluded activity will not get config changes.
621      */
622     @Test
testAppOrientationWhenRotating()623     public void testAppOrientationWhenRotating() throws Exception {
624         assumeTrue("Skipping test: no rotation support", supportsRotation());
625 
626         // Start resizeable activity that handles configuration changes.
627         separateTestJournal();
628         launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
629         launchActivity(RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
630         mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
631 
632         final int displayId = mWmState.getDisplayByActivity(RESIZEABLE_ACTIVITY);
633 
634         // Rotate the activity and check that it receives configuration changes with a different
635         // orientation each time.
636         final RotationSession rotationSession = createManagedRotationSession();
637         assumeTrue("Skipping test: no locked user rotation mode support.",
638                 supportsLockedUserRotation(rotationSession, displayId));
639 
640         rotationSession.set(ROTATION_0);
641         SizeInfo reportedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
642         int prevOrientation = reportedSizes.orientation;
643 
644         final int[] rotations = { ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0 };
645         for (final int rotation : rotations) {
646             separateTestJournal();
647             rotationSession.set(rotation);
648 
649             // Verify lifecycle count and orientation changes.
650             assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */,
651                     1 /* numConfigChange */);
652             reportedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
653             assertNotEquals(prevOrientation, reportedSizes.orientation);
654             assertRelaunchOrConfigChanged(TEST_ACTIVITY, 0 /* numRelaunch */,
655                     0 /* numConfigChange */);
656 
657             prevOrientation = reportedSizes.orientation;
658         }
659     }
660 
661     /**
662      * Test that the orientation for a simulated display context derived from an application context
663      * will not change when the device rotates.
664      */
665     @Test
testAppContextDerivedDisplayContextOrientationWhenRotating()666     public void testAppContextDerivedDisplayContextOrientationWhenRotating() {
667         assumeTrue("Skipping test: no rotation support", supportsRotation());
668         assumeTrue("Skipping test: no multi-display support", supportsMultiDisplay());
669 
670         assertDisplayContextDoesntChangeOrientationWhenRotating(Activity::getApplicationContext);
671     }
672 
673     /**
674      * Test that the orientation for a simulated display context derived from an activity context
675      * will not change when the device rotates.
676      */
677     @Test
testActivityContextDerivedDisplayContextOrientationWhenRotating()678     public void testActivityContextDerivedDisplayContextOrientationWhenRotating() {
679         assumeTrue("Skipping test: no rotation support", supportsRotation());
680         assumeTrue("Skipping test: no multi-display support", supportsMultiDisplay());
681 
682         assertDisplayContextDoesntChangeOrientationWhenRotating(activity -> activity);
683     }
684 
685     /**
686      * Asserts that the orientation for a simulated display context derived from a base context will
687      * not change when the device rotates.
688      *
689      * @param baseContextSupplier function that returns a base context used to created the display
690      *                            context.
691      *
692      * @see #testAppContextDerivedDisplayContextOrientationWhenRotating
693      * @see #testActivityContextDerivedDisplayContextOrientationWhenRotating
694      */
assertDisplayContextDoesntChangeOrientationWhenRotating( Function<Activity, Context> baseContextSupplier)695     private void assertDisplayContextDoesntChangeOrientationWhenRotating(
696             Function<Activity, Context> baseContextSupplier) {
697         RotationSession rotationSession = createManagedRotationSession();
698         rotationSession.set(ROTATION_0);
699 
700         TestActivitySession<ConfigChangeHandlingActivity> activitySession
701                 = createManagedTestActivitySession();
702         activitySession.launchTestActivityOnDisplaySync(
703                 ConfigChangeHandlingActivity.class,
704                 Display.DEFAULT_DISPLAY,
705                 WINDOWING_MODE_FULLSCREEN);
706         final ConfigChangeHandlingActivity activity = activitySession.getActivity();
707 
708         VirtualDisplaySession virtualDisplaySession = createManagedVirtualDisplaySession();
709         WindowManagerState.DisplayContent displayContent = virtualDisplaySession
710                 .setSimulateDisplay(true)
711                 .setSimulationDisplaySize(100 /* width */, 200 /* height */)
712                 .createDisplay();
713 
714         DisplayManager dm = activity.getSystemService(DisplayManager.class);
715         Display simulatedDisplay = dm.getDisplay(displayContent.mId);
716         Context simulatedDisplayContext = baseContextSupplier.apply(activity)
717                 .createDisplayContext(simulatedDisplay);
718         assertEquals(ORIENTATION_PORTRAIT,
719                 simulatedDisplayContext.getResources().getConfiguration().orientation);
720 
721         separateTestJournal();
722 
723         final int[] rotations = {ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0};
724         for (final int rotation : rotations) {
725             rotationSession.set(rotation);
726 
727             assertRelaunchOrConfigChanged(activity.getComponentName(), 0 /* numRelaunch */,
728                     1 /* numConfigChange */);
729             separateTestJournal();
730 
731             assertEquals("Display context orientation must not be changed", ORIENTATION_PORTRAIT,
732                     simulatedDisplayContext.getResources().getConfiguration().orientation);
733         }
734     }
735 
736     /**
737      * Test that activity orientation will not change when trying to rotate fixed-orientation
738      * activity.
739      * Also verify that occluded activity will not get config changes.
740      */
741     @Test
testFixedOrientationWhenRotating()742     public void testFixedOrientationWhenRotating() {
743         assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest());
744         // TODO(b/110533226): Fix test on devices with display cutout
745         assumeFalse("Skipping test: display cutout present, can't predict exact lifecycle",
746                 hasDisplayCutout());
747 
748         // Start portrait-fixed activity
749         separateTestJournal();
750         final ActivitySession activitySession = createManagedActivityClientSession()
751                 .startActivity(getLaunchActivityBuilder()
752                         .setUseInstrumentation()
753                         .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
754                         .setTargetActivity(RESIZEABLE_ACTIVITY));
755         mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
756 
757         final int displayId = mWmState.getDisplayByActivity(RESIZEABLE_ACTIVITY);
758 
759         final RotationSession rotationSession = createManagedRotationSession();
760         assumeTrue("Skipping test: no user locked rotation support.",
761                 supportsLockedUserRotation(rotationSession, displayId));
762 
763         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
764         mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
765         final SizeInfo initialSize = getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY);
766 
767         // Rotate the display and check that the orientation doesn't change
768         rotationSession.set(ROTATION_0);
769         final int[] rotations = { ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0 };
770         for (final int rotation : rotations) {
771             separateTestJournal();
772             rotationSession.set(rotation, false /* waitDeviceRotation */);
773 
774             // Verify lifecycle count and orientation changes.
775             assertRelaunchOrConfigChanged(PORTRAIT_ORIENTATION_ACTIVITY, 0 /* numRelaunch */,
776                     0 /* numConfigChange */);
777             final SizeInfo currentSize = activitySession.getConfigInfo().sizeInfo;
778             assertEquals("Sizes must not be changed", initialSize, currentSize);
779             assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */,
780                     0 /* numConfigChange */);
781         }
782     }
783 
784     /**
785      * Test that device handles moving between two tasks with different orientations.
786      */
787     @Test
testTaskMoveToBackOrientation()788     public void testTaskMoveToBackOrientation() {
789         assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest());
790 
791         // Start landscape activity.
792         launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
793         mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */);
794         mWmState.waitAndAssertLastOrientation("Fullscreen app requested landscape orientation",
795                 SCREEN_ORIENTATION_LANDSCAPE);
796 
797         // Start another activity in a different task.
798         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
799 
800         // Request portrait
801         mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT);
802         mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
803         waitForBroadcastActivityReady(ORIENTATION_PORTRAIT);
804 
805         // Finish activity
806         mBroadcastActionTrigger.moveTopTaskToBack();
807 
808         // Verify that activity brought to front is in originally requested orientation.
809         mWmState.waitForValidState(LANDSCAPE_ORIENTATION_ACTIVITY);
810         mWmState.waitAndAssertLastOrientation("Should return to app in landscape orientation",
811                 SCREEN_ORIENTATION_LANDSCAPE);
812     }
813 
814     /**
815      * Test that device doesn't change device orientation by app request while in multi-window.
816      */
817     @Test
testSplitscreenPortraitAppOrientationRequests()818     public void testSplitscreenPortraitAppOrientationRequests() throws Exception {
819         requestOrientationInSplitScreen(createManagedRotationSession(),
820                 isDisplayPortrait() ? ROTATION_0 : ROTATION_90, LANDSCAPE_ORIENTATION_ACTIVITY);
821     }
822 
823     /**
824      * Test that device doesn't change device orientation by app request while in multi-window.
825      */
826     @Test
testSplitscreenLandscapeAppOrientationRequests()827     public void testSplitscreenLandscapeAppOrientationRequests() throws Exception {
828         requestOrientationInSplitScreen(createManagedRotationSession(),
829                 isDisplayPortrait() ? ROTATION_90 : ROTATION_0, PORTRAIT_ORIENTATION_ACTIVITY);
830     }
831 
832     /**
833      * Launch specified activity in split-screen then rotate the divice, checking orientation
834      * should always change by the device rotation.
835      */
requestOrientationInSplitScreen(RotationSession rotationSession, int rotation, ComponentName activity)836     private void requestOrientationInSplitScreen(RotationSession rotationSession, int rotation,
837             ComponentName activity) throws Exception {
838         assumeTrue("Skipping test: no rotation support", supportsRotation());
839         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
840 
841         // Launch activities in split screen.
842         launchActivitiesInSplitScreen(
843                 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
844                 getLaunchActivityBuilder().setTargetActivity(activity));
845         mWmState.assertVisibility(activity, true /* visible */);
846 
847         // Rotate the device and it should always rotate regardless orientation app requested.
848         rotationSession.set(rotation);
849         assertEquals("Split-screen apps shouldn't influence device orientation",
850                 rotation, mWmState.getRotation());
851 
852         // Launch target activity during split-screen again and check orientation still not change.
853         mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId());
854         getLaunchActivityBuilder().setMultipleTask(true).setTargetActivity(activity).execute();
855         mWmState.computeState(activity);
856         mWmState.assertVisibility(activity, true /* visible */);
857         assertEquals("Split-screen apps shouldn't influence device orientation",
858                 rotation, mWmState.getRotation());
859     }
860 
861     /**
862      * Asserts that after rotation, the aspect ratios of display size, metrics, and configuration
863      * have flipped.
864      */
assertSizesRotate(SizeInfo rotationA, SizeInfo rotationB, boolean skipOrientationCheck)865     private static void assertSizesRotate(SizeInfo rotationA, SizeInfo rotationB,
866             boolean skipOrientationCheck) {
867         assertEquals(rotationA.displayWidth, rotationA.metricsWidth);
868         assertEquals(rotationA.displayHeight, rotationA.metricsHeight);
869         assertEquals(rotationB.displayWidth, rotationB.metricsWidth);
870         assertEquals(rotationB.displayHeight, rotationB.metricsHeight);
871 
872         if (skipOrientationCheck) {
873             // All done if we are not doing orientation check.
874             return;
875         }
876         final boolean beforePortrait = rotationA.displayWidth < rotationA.displayHeight;
877         final boolean afterPortrait = rotationB.displayWidth < rotationB.displayHeight;
878         assertFalse(beforePortrait == afterPortrait);
879 
880         final boolean beforeConfigPortrait = rotationA.widthDp < rotationA.heightDp;
881         final boolean afterConfigPortrait = rotationB.widthDp < rotationB.heightDp;
882         assertEquals(beforePortrait, beforeConfigPortrait);
883         assertEquals(afterPortrait, afterConfigPortrait);
884 
885         assertEquals(rotationA.smallestWidthDp, rotationB.smallestWidthDp);
886     }
887 
888     /**
889      * Throws an AssertionError if fullscreenSizes has widths/heights (depending on aspect ratio)
890      * that are smaller than the dockedSizes.
891      */
892     private static void assertSizesAreSane(SizeInfo fullscreenSizes, SizeInfo dockedSizes) {
893         final boolean isHorizontalDivision =
894                 fullscreenSizes.displayHeight - dockedSizes.displayHeight >
895                 fullscreenSizes.displayWidth - dockedSizes.displayWidth;
896         if (isHorizontalDivision) {
897             assertThat(dockedSizes.displayHeight, lessThan(fullscreenSizes.displayHeight));
898             assertThat(dockedSizes.heightDp, lessThan(fullscreenSizes.heightDp));
899             assertThat(dockedSizes.metricsHeight, lessThan(fullscreenSizes.metricsHeight));
900         } else {
901             assertThat(dockedSizes.displayWidth, lessThan(fullscreenSizes.displayWidth));
902             assertThat(dockedSizes.widthDp, lessThan(fullscreenSizes.widthDp));
903             assertThat(dockedSizes.metricsWidth, lessThan(fullscreenSizes.metricsWidth));
904         }
905     }
906 
907     /**
908      * Throws an AssertionError if sizes are different.
909      */
910     private static void assertSizesAreSame(SizeInfo firstSize, SizeInfo secondSize) {
911         assertEquals(firstSize.widthDp, secondSize.widthDp);
912         assertEquals(firstSize.heightDp, secondSize.heightDp);
913         assertEquals(firstSize.displayWidth, secondSize.displayWidth);
914         assertEquals(firstSize.displayHeight, secondSize.displayHeight);
915         assertEquals(firstSize.metricsWidth, secondSize.metricsWidth);
916         assertEquals(firstSize.metricsHeight, secondSize.metricsHeight);
917         assertEquals(firstSize.smallestWidthDp, secondSize.smallestWidthDp);
918     }
919 
920     private void waitForBroadcastActivityReady(int orientation) {
921         mWmState.waitForActivityOrientation(BROADCAST_RECEIVER_ACTIVITY, orientation);
922         mWmState.waitForActivityState(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED);
923     }
924 
925     /**
926      * Test launching an activity which requests specific UI mode during creation.
927      */
928     @Test
929     public void testLaunchWithUiModeChange() {
930         // Launch activity that changes UI mode and handles this configuration change.
931         launchActivity(NIGHT_MODE_ACTIVITY);
932         mWmState.waitForActivityState(NIGHT_MODE_ACTIVITY, STATE_RESUMED);
933 
934         // Check if activity is launched successfully.
935         mWmState.assertVisibility(NIGHT_MODE_ACTIVITY, true /* visible */);
936         mWmState.assertFocusedActivity("Launched activity should be focused",
937                 NIGHT_MODE_ACTIVITY);
938         mWmState.assertResumedActivity("Launched activity must be resumed", NIGHT_MODE_ACTIVITY);
939     }
940 
941     @Test
942     public void testAppConfigurationMatchesActivityInMultiWindow() throws Exception {
943         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
944 
945         final ActivitySession activitySession = createManagedActivityClientSession()
946                 .startActivity(getLaunchActivityBuilder()
947                         .setUseInstrumentation()
948                         .setTargetActivity(RESIZEABLE_ACTIVITY)
949                         .setWindowingMode(WINDOWING_MODE_FULLSCREEN));
950         putActivityInPrimarySplit(RESIZEABLE_ACTIVITY);
951         SizeInfo dockedActivitySizes = getActivitySizeInfo(activitySession);
952         SizeInfo applicationSizes = getAppSizeInfo(activitySession);
953         assertSizesAreSame(dockedActivitySizes, applicationSizes);
954 
955         // Move the activity to fullscreen and check that the size was updated
956         separateTestJournal();
957         mTaskOrganizer.dismissSplitScreen(true /* primaryOnTop */);
958         waitForOrFail("Activity and application configuration must match",
959                 () -> activityAndAppSizesMatch(activitySession));
960         final SizeInfo fullscreenSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
961         applicationSizes = getAppSizeInfo(activitySession);
962         assertSizesAreSane(fullscreenSizes, dockedActivitySizes);
963         assertSizesAreSame(fullscreenSizes, applicationSizes);
964 
965         // Move the activity to docked size again, check if the sizes were updated
966         separateTestJournal();
967         putActivityInPrimarySplit(RESIZEABLE_ACTIVITY);
968         waitForOrFail("Activity and application configuration must match",
969                 () -> activityAndAppSizesMatch(activitySession));
970         dockedActivitySizes = getActivitySizeInfo(activitySession);
971         applicationSizes = getAppSizeInfo(activitySession);
972         assertSizesAreSane(fullscreenSizes, dockedActivitySizes);
973         assertSizesAreSame(dockedActivitySizes, applicationSizes);
974     }
975 
976     @Test
977     public void testAppConfigurationMatchesTopActivityInMultiWindow() throws Exception {
978         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
979 
980         // Launch initial activity in fullscreen and assert sizes
981         final ActivitySession fullscreenActivitySession = createManagedActivityClientSession()
982                 .startActivity(getLaunchActivityBuilder()
983                         .setUseInstrumentation()
984                         .setTargetActivity(TEST_ACTIVITY)
985                         .setWindowingMode(WINDOWING_MODE_FULLSCREEN));
986         SizeInfo fullscreenActivitySizes = getActivitySizeInfo(fullscreenActivitySession);
987         SizeInfo applicationSizes = getAppSizeInfo(fullscreenActivitySession);
988         assertSizesAreSame(fullscreenActivitySizes, applicationSizes);
989 
990         // Launch second activity in split-screen and assert that sizes were updated
991         separateTestJournal();
992         final ActivitySession secondActivitySession = createManagedActivityClientSession()
993                 .startActivity(getLaunchActivityBuilder()
994                         .setUseInstrumentation()
995                         .setTargetActivity(RESIZEABLE_ACTIVITY)
996                         .setNewTask(true)
997                         .setMultipleTask(true));
998         putActivityInPrimarySplit(RESIZEABLE_ACTIVITY);
999         waitForOrFail("Activity and application configuration must match",
1000                 () -> activityAndAppSizesMatch(secondActivitySession));
1001         SizeInfo dockedActivitySizes = getActivitySizeInfo(secondActivitySession);
1002         applicationSizes = getAppSizeInfo(secondActivitySession);
1003         assertSizesAreSame(dockedActivitySizes, applicationSizes);
1004         assertSizesAreSane(fullscreenActivitySizes, dockedActivitySizes);
1005 
1006         // Launch third activity in secondary split-screen and assert that sizes were updated
1007         separateTestJournal();
1008         final ActivitySession thirdActivitySession = createManagedActivityClientSession()
1009                 .startActivity(getLaunchActivityBuilder()
1010                         .setUseInstrumentation()
1011                         .setTargetActivity(RESIZEABLE_ACTIVITY)
1012                         .setNewTask(true)
1013                         .setMultipleTask(true));
1014         putActivityInPrimarySplit(RESIZEABLE_ACTIVITY);
1015         waitForOrFail("Activity and application configuration must match",
1016                 () -> activityAndAppSizesMatch(thirdActivitySession));
1017         SizeInfo secondarySplitActivitySizes = getActivitySizeInfo(thirdActivitySession);
1018         applicationSizes = getAppSizeInfo(thirdActivitySession);
1019         assertSizesAreSame(secondarySplitActivitySizes, applicationSizes);
1020         assertSizesAreSane(fullscreenActivitySizes, secondarySplitActivitySizes);
1021     }
1022 
1023     @Test
testAppConfigurationMatchesActivityInFreeform()1024     public void testAppConfigurationMatchesActivityInFreeform() throws Exception {
1025         assumeTrue("Skipping test: no freeform support", supportsFreeform());
1026 
1027         // Launch activity in freeform and assert sizes
1028         final ActivitySession freeformActivitySession = createManagedActivityClientSession()
1029                 .startActivity(getLaunchActivityBuilder()
1030                         .setUseInstrumentation()
1031                         .setTargetActivity(TEST_ACTIVITY)
1032                         .setWindowingMode(WINDOWING_MODE_FREEFORM));
1033         SizeInfo freeformActivitySizes = getActivitySizeInfo(freeformActivitySession);
1034         SizeInfo applicationSizes = getAppSizeInfo(freeformActivitySession);
1035         assertSizesAreSame(freeformActivitySizes, applicationSizes);
1036     }
1037 
activityAndAppSizesMatch(ActivitySession activitySession)1038     private boolean activityAndAppSizesMatch(ActivitySession activitySession) {
1039         final SizeInfo activitySize = activitySession.getConfigInfo().sizeInfo;
1040         final SizeInfo appSize = activitySession.getAppConfigInfo().sizeInfo;
1041         return activitySize.equals(appSize);
1042     }
1043 
getActivitySizeInfo(ActivitySession activitySession)1044     private SizeInfo getActivitySizeInfo(ActivitySession activitySession) {
1045         return activitySession.getConfigInfo().sizeInfo;
1046     }
1047 
getAppSizeInfo(ActivitySession activitySession)1048     private SizeInfo getAppSizeInfo(ActivitySession activitySession) {
1049         return activitySession.getAppConfigInfo().sizeInfo;
1050     }
1051 }
1052