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