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