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