• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package android.server.wm;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
22 import static android.server.wm.ComponentNameUtils.getWindowName;
23 import static android.server.wm.ShellCommandHelper.executeShellCommand;
24 import static android.server.wm.StateLogger.logE;
25 import static android.server.wm.WindowManagerState.STATE_RESUMED;
26 import static android.server.wm.WindowManagerState.STATE_STOPPED;
27 import static android.server.wm.WindowManagerState.TRANSIT_TASK_CLOSE;
28 import static android.server.wm.WindowManagerState.TRANSIT_TASK_OPEN;
29 import static android.server.wm.app.Components.BOTTOM_ACTIVITY;
30 import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
31 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
32 import static android.server.wm.app.Components.LAUNCH_TEST_ON_DESTROY_ACTIVITY;
33 import static android.server.wm.app.Components.RESIZEABLE_ACTIVITY;
34 import static android.server.wm.app.Components.SHOW_WHEN_LOCKED_ATTR_ACTIVITY;
35 import static android.server.wm.app.Components.TEST_ACTIVITY;
36 import static android.server.wm.app.Components.TOAST_ACTIVITY;
37 import static android.server.wm.app.Components.VIRTUAL_DISPLAY_ACTIVITY;
38 import static android.server.wm.app27.Components.SDK_27_LAUNCHING_ACTIVITY;
39 import static android.server.wm.app27.Components.SDK_27_SEPARATE_PROCESS_ACTIVITY;
40 import static android.server.wm.app27.Components.SDK_27_TEST_ACTIVITY;
41 import static android.server.wm.lifecycle.ActivityStarterTests.StandardActivity;
42 import static android.view.Display.DEFAULT_DISPLAY;
43 
44 import static org.junit.Assert.assertEquals;
45 import static org.junit.Assert.assertFalse;
46 import static org.junit.Assert.assertNotNull;
47 import static org.junit.Assert.assertTrue;
48 import static org.junit.Assert.fail;
49 import static org.junit.Assume.assumeFalse;
50 import static org.junit.Assume.assumeTrue;
51 
52 import android.platform.test.annotations.Presubmit;
53 import android.server.wm.CommandSession.ActivityCallback;
54 import android.server.wm.CommandSession.ActivitySession;
55 import android.server.wm.CommandSession.SizeInfo;
56 import android.server.wm.WindowManagerState.DisplayContent;
57 import android.server.wm.WindowManagerState.Task;
58 
59 import org.junit.Before;
60 import org.junit.Test;
61 
62 /**
63  * Build/Install/Run:
64  *     atest CtsWindowManagerDeviceTestCases:MultiDisplayPolicyTests
65  *
66  * Tests each expected policy on multi-display environment.
67  */
68 @Presubmit
69 @android.server.wm.annotation.Group3
70 public class MultiDisplayPolicyTests extends MultiDisplayTestBase {
71 
72     @Before
73     @Override
setUp()74     public void setUp() throws Exception {
75         super.setUp();
76         assumeTrue(supportsMultiDisplay());
77     }
78     /**
79      * Tests that all activities that were on the private display are destroyed on display removal.
80      */
81     @Test
testContentDestroyOnDisplayRemoved()82     public void testContentDestroyOnDisplayRemoved() {
83         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
84             // Create new private virtual display.
85             final DisplayContent newDisplay = virtualDisplaySession.createDisplay();
86             mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
87 
88             // Launch activities on new secondary display.
89             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
90             waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
91                     "Launched activity must be resumed");
92 
93             launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
94             waitAndAssertActivityStateOnDisplay(RESIZEABLE_ACTIVITY, STATE_RESUMED, newDisplay.mId,
95                     "Launched activity must be resumed");
96 
97             separateTestJournal();
98             // Destroy the display and check if activities are removed from system.
99         }
100 
101         mWmState.waitForActivityRemoved(TEST_ACTIVITY);
102         mWmState.waitForActivityRemoved(RESIZEABLE_ACTIVITY);
103 
104         // Check AM state.
105         assertFalse("Activity from removed display must be destroyed",
106                 mWmState.containsActivity(TEST_ACTIVITY));
107         assertFalse("Activity from removed display must be destroyed",
108                 mWmState.containsActivity(RESIZEABLE_ACTIVITY));
109         // Check WM state.
110         assertFalse("Activity windows from removed display must be destroyed",
111                 mWmState.containsWindow(getWindowName(TEST_ACTIVITY)));
112         assertFalse("Activity windows from removed display must be destroyed",
113                 mWmState.containsWindow(getWindowName(RESIZEABLE_ACTIVITY)));
114         // Check activity logs.
115         assertActivityDestroyed(TEST_ACTIVITY);
116         assertActivityDestroyed(RESIZEABLE_ACTIVITY);
117     }
118 
119     /**
120      * Tests that newly launched activity will be landing on default display on display removal.
121      */
122     @Test
testActivityLaunchOnContentDestroyDisplayRemoved()123     public void testActivityLaunchOnContentDestroyDisplayRemoved() {
124         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
125             // Create new private virtual display.
126             final DisplayContent newDisplay = virtualDisplaySession.createDisplay();
127             mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
128 
129             // Launch activities on new secondary display.
130             launchActivityOnDisplay(LAUNCH_TEST_ON_DESTROY_ACTIVITY, newDisplay.mId);
131 
132             waitAndAssertActivityStateOnDisplay(LAUNCH_TEST_ON_DESTROY_ACTIVITY, STATE_RESUMED,
133                     newDisplay.mId,"Launched activity must be resumed on secondary display");
134 
135             // Destroy the display
136         }
137 
138         waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
139                 "Newly launches activity should be landing on default display");
140     }
141 
142     /**
143      * Tests that the update of display metrics updates all its content.
144      */
145     @Test
testDisplayResize()146     public void testDisplayResize() {
147         final VirtualDisplaySession virtualDisplaySession = createManagedVirtualDisplaySession();
148         // Create new virtual display.
149         final DisplayContent newDisplay = virtualDisplaySession.createDisplay();
150         mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
151 
152         // Launch a resizeable activity on new secondary display.
153         separateTestJournal();
154         launchActivityOnDisplay(RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN, newDisplay.mId);
155         waitAndAssertActivityStateOnDisplay(RESIZEABLE_ACTIVITY, STATE_RESUMED, newDisplay.mId,
156                 "Launched activity must be resumed");
157 
158         // Grab reported sizes and compute new with slight size change.
159         final SizeInfo initialSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
160 
161         // Resize the display
162         separateTestJournal();
163         virtualDisplaySession.resizeDisplay();
164 
165         mWmState.waitForWithAmState(amState -> {
166             try {
167                 return amState.hasActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED)
168                         && new ActivityLifecycleCounts(RESIZEABLE_ACTIVITY)
169                                 .getCount(ActivityCallback.ON_CONFIGURATION_CHANGED) == 1;
170             } catch (Exception e) {
171                 logE("Error waiting for valid state: " + e.getMessage());
172                 return false;
173             }
174         }, "the configuration change to happen and activity to be resumed");
175 
176         mWmState.computeState(
177                 new WaitForValidActivityState(RESIZEABLE_ACTIVITY),
178                 new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
179         mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
180         mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true);
181 
182         // Check if activity in virtual display was resized properly.
183         assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */,
184                 1 /* numConfigChange */);
185 
186         final SizeInfo updatedSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
187         assertTrue(updatedSize.widthDp <= initialSize.widthDp);
188         assertTrue(updatedSize.heightDp <= initialSize.heightDp);
189         assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2);
190         assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2);
191     }
192 
193     /**
194      * Tests that when primary display is rotated secondary displays are not affected.
195      */
196     @Test
testRotationNotAffectingSecondaryScreen()197     public void testRotationNotAffectingSecondaryScreen() {
198         final VirtualDisplayLauncher virtualLauncher =
199                 mObjectTracker.manage(new VirtualDisplayLauncher());
200         // Create new virtual display.
201         final DisplayContent newDisplay = virtualLauncher.setResizeDisplay(false).createDisplay();
202         mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
203 
204         // Launch activity on new secondary display.
205         final ActivitySession resizeableActivitySession =
206                 virtualLauncher.launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay);
207         waitAndAssertActivityStateOnDisplay(RESIZEABLE_ACTIVITY, STATE_RESUMED, newDisplay.mId,
208                 "Top activity must be on secondary display");
209         final SizeInfo initialSize = resizeableActivitySession.getConfigInfo().sizeInfo;
210 
211         assertNotNull("Test activity must have reported initial size on launch", initialSize);
212 
213         final RotationSession rotationSession = createManagedRotationSession();
214         // Rotate primary display and check that activity on secondary display is not affected.
215         rotateAndCheckSameSizes(rotationSession, resizeableActivitySession, initialSize);
216 
217         // Launch activity to secondary display when primary one is rotated.
218         final int initialRotation = mWmState.getRotation();
219         rotationSession.set((initialRotation + 1) % 4);
220 
221         final ActivitySession testActivitySession =
222                 virtualLauncher.launchActivityOnDisplay(TEST_ACTIVITY, newDisplay);
223         waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
224                 "Top activity must be on secondary display");
225         final SizeInfo testActivitySize = testActivitySession.getConfigInfo().sizeInfo;
226 
227         assertEquals("Sizes of secondary display must not change after rotation of primary"
228                 + " display", initialSize, testActivitySize);
229     }
230 
rotateAndCheckSameSizes(RotationSession rotationSession, ActivitySession activitySession, SizeInfo initialSize)231     private void rotateAndCheckSameSizes(RotationSession rotationSession,
232             ActivitySession activitySession, SizeInfo initialSize) {
233         for (int rotation = 3; rotation >= 0; --rotation) {
234             rotationSession.set(rotation);
235             final SizeInfo rotatedSize = activitySession.getConfigInfo().sizeInfo;
236 
237             assertEquals("Sizes must not change after rotation", initialSize, rotatedSize);
238         }
239     }
240 
241     /**
242      * Tests that turning the primary display off does not affect the activity running
243      * on an external secondary display.
244      */
245     @Test
testExternalDisplayActivityTurnPrimaryOff()246     public void testExternalDisplayActivityTurnPrimaryOff() {
247         // Launch something on the primary display so we know there is a resumed activity there
248         launchActivity(RESIZEABLE_ACTIVITY);
249         waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
250                 "Activity launched on primary display must be resumed");
251 
252         final DisplayContent newDisplay = createManagedExternalDisplaySession()
253                 .setCanShowWithInsecureKeyguard(true).createVirtualDisplay();
254 
255         launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
256 
257         // Check that the activity is launched onto the external display
258         waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
259                 "Activity launched on external display must be resumed");
260         mWmState.assertFocusedAppOnDisplay("App on default display must still be focused",
261                 RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY);
262 
263         separateTestJournal();
264         mObjectTracker.manage(new PrimaryDisplayStateSession()).turnScreenOff();
265 
266         // Wait for the fullscreen stack to start sleeping, and then make sure the
267         // test activity is still resumed.
268         final ActivityLifecycleCounts counts = new ActivityLifecycleCounts(RESIZEABLE_ACTIVITY);
269         if (!Condition.waitFor(counts.countWithRetry(RESIZEABLE_ACTIVITY + " to be stopped",
270                 countSpec(ActivityCallback.ON_STOP, CountSpec.EQUALS, 1)))) {
271             fail(RESIZEABLE_ACTIVITY + " has received "
272                     + counts.getCount(ActivityCallback.ON_STOP)
273                     + " onStop() calls, expecting 1");
274         }
275         // For this test we create this virtual display with flag showContentWhenLocked, so it
276         // cannot be effected when default display screen off.
277         waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
278                 "Activity launched on external display must be resumed");
279     }
280 
281     /**
282      * Tests that turning the secondary display off stops activities running and makes invisible
283      * on that display.
284      */
285     @Test
testExternalDisplayToggleState()286     public void testExternalDisplayToggleState() {
287         final ExternalDisplaySession externalDisplaySession = createManagedExternalDisplaySession();
288         final DisplayContent newDisplay = externalDisplaySession.createVirtualDisplay();
289 
290         launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
291 
292         // Check that the test activity is resumed on the external display
293         waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
294                 "Activity launched on external display must be resumed");
295 
296         externalDisplaySession.turnDisplayOff();
297 
298         // Check that turning off the external display stops the activity, and makes it
299         // invisible.
300         waitAndAssertActivityState(TEST_ACTIVITY, STATE_STOPPED,
301                 "Activity launched on external display must be stopped after turning off");
302         mWmState.assertVisibility(TEST_ACTIVITY, false /* visible */);
303 
304         externalDisplaySession.turnDisplayOn();
305 
306         // Check that turning on the external display resumes the activity
307         waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
308                 "Activity launched on external display must be resumed");
309     }
310 
311     /**
312      * Tests no leaking after external display removed.
313      */
314     @Test
testNoLeakOnExternalDisplay()315     public void testNoLeakOnExternalDisplay() throws Exception {
316         // How this test works:
317         // When receiving the request to remove a display and some activities still exist on that
318         // display, it will finish those activities first, so the display won't be removed
319         // immediately. Then, when all activities were destroyed, the display removes itself.
320 
321         // Get display count before testing, as some devices may have more than one built-in
322         // display.
323         mWmState.computeState();
324         final int displayCount = mWmState.getDisplayCount();
325         try (final VirtualDisplaySession externalDisplaySession = new VirtualDisplaySession()) {
326             final DisplayContent newDisplay = externalDisplaySession
327                     .setSimulateDisplay(true).createDisplay();
328             launchActivityOnDisplay(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
329             waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId,
330                     "Virtual activity should be Top Resumed Activity.");
331             mWmState.assertFocusedAppOnDisplay("Activity on second display must be focused.",
332                     VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
333         }
334         mWmState.waitFor((amState) -> amState.getDisplayCount() == displayCount,
335                 "external displays to be removed");
336         assertEquals(displayCount, mWmState.getDisplayCount());
337         assertEquals(displayCount, mWmState.getKeyguardControllerState().
338                 mKeyguardOccludedStates.size());
339     }
340 
341     /**
342      * Tests launching activities on secondary and then on primary display to see if the stack
343      * visibility is not affected.
344      */
345     @Test
testLaunchActivitiesAffectsVisibility()346     public void testLaunchActivitiesAffectsVisibility() {
347         // Start launching activity.
348         launchActivity(LAUNCHING_ACTIVITY);
349 
350         // Create new virtual display.
351         final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
352         mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
353 
354         // Launch activity on new secondary display.
355         launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
356         mWmState.assertVisibility(TEST_ACTIVITY, true /* visible */);
357         mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
358 
359         // Launch activity on primary display and check if it doesn't affect activity on
360         // secondary display.
361         getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY).execute();
362         mWmState.waitForValidState(RESIZEABLE_ACTIVITY);
363         mWmState.assertVisibility(TEST_ACTIVITY, true /* visible */);
364         mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
365         assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, RESIZEABLE_ACTIVITY),
366                 pair(newDisplay.mId, TEST_ACTIVITY));
367     }
368 
369     /**
370      * Test that move-task works when moving between displays.
371      */
372     @Test
testMoveTaskBetweenDisplays()373     public void testMoveTaskBetweenDisplays() {
374         // Create new virtual display.
375         final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
376         mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
377         mWmState.assertFocusedActivity("Virtual display activity must be on top",
378                 VIRTUAL_DISPLAY_ACTIVITY);
379         final int defaultDisplayStackId = mWmState.getFocusedTaskId();
380         Task frontStack = mWmState.getRootTask(
381                 defaultDisplayStackId);
382         assertEquals("Top stack must remain on primary display",
383                 DEFAULT_DISPLAY, frontStack.mDisplayId);
384 
385         // Launch activity on new secondary display.
386         launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
387 
388         waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
389                 "Top activity must be on secondary display");
390         assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, VIRTUAL_DISPLAY_ACTIVITY),
391                 pair(newDisplay.mId, TEST_ACTIVITY));
392 
393         // Move activity from secondary display to primary.
394         moveActivityToRootTaskOrOnTop(TEST_ACTIVITY, defaultDisplayStackId);
395         waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
396                 "Moved activity must be on top");
397     }
398 
399     /**
400      * Tests launching activities on secondary display and then removing it to see if stack focus
401      * is moved correctly.
402      * This version launches virtual display creator to fullscreen stack in split-screen.
403      */
404     @Test
testStackFocusSwitchOnDisplayRemoved()405     public void testStackFocusSwitchOnDisplayRemoved() {
406         assumeTrue(supportsSplitScreenMultiWindow());
407 
408         // Start launching activity into docked stack.
409         launchActivitiesInSplitScreen(
410                 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
411                 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
412         mWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
413 
414         tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
415                 WINDOWING_MODE_MULTI_WINDOW);
416     }
417 
418     /**
419      * Tests launching activities on secondary display and then removing it to see if stack focus
420      * is moved correctly.
421      * This version launches virtual display creator to docked stack in split-screen.
422      */
423     @Test
testStackFocusSwitchOnDisplayRemoved2()424     public void testStackFocusSwitchOnDisplayRemoved2() {
425         assumeTrue(supportsSplitScreenMultiWindow());
426 
427         // Setup split-screen.
428         launchActivitiesInSplitScreen(
429                 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY),
430                 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY));
431         mWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
432 
433         tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
434                 WINDOWING_MODE_MULTI_WINDOW);
435     }
436 
437     /**
438      * Tests launching activities on secondary display and then removing it to see if stack focus
439      * is moved correctly.
440      * This version works without split-screen.
441      */
442     @Test
testStackFocusSwitchOnDisplayRemoved3()443     public void testStackFocusSwitchOnDisplayRemoved3() {
444         // Start an activity on default display to determine default stack.
445         launchActivity(BROADCAST_RECEIVER_ACTIVITY);
446         final int focusedStackWindowingMode = mWmState.getFrontRootTaskWindowingMode(
447                 DEFAULT_DISPLAY);
448         // Finish probing activity.
449         mBroadcastActionTrigger.finishBroadcastReceiverActivity();
450 
451         tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */,
452                 focusedStackWindowingMode);
453     }
454 
455     /**
456      * Create a virtual display, launch a test activity there, destroy the display and check if test
457      * activity is moved to a stack on the default display.
458      */
tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int windowingMode)459     private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int windowingMode) {
460         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
461             // Create new virtual display.
462             final DisplayContent newDisplay = virtualDisplaySession
463                     .setPublicDisplay(true)
464                     .setLaunchInSplitScreen(splitScreen)
465                     .createDisplay();
466             if (splitScreen) {
467                 // Set the secondary split root task as launch root to verify remaining tasks will
468                 // be reparented to matching launch root after removed the virtual display.
469                 mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId());
470                 // Launch activity on new secondary display in fullscreen to put it in splitscreen
471                 // after the display's disconenction.
472                 launchActivityOnDisplay(
473                         RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN, newDisplay.mId);
474             } else {
475                 // Launch activity on new secondary display
476                 launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
477             }
478 
479             waitAndAssertActivityStateOnDisplay(RESIZEABLE_ACTIVITY, STATE_RESUMED, newDisplay.mId,
480                     "Test activity must be on secondary display");
481 
482             separateTestJournal();
483             // Destroy virtual display.
484         }
485 
486         mWmState.computeState();
487         assertActivityLifecycle(RESIZEABLE_ACTIVITY, false /* relaunched */);
488         mWmState.waitForValidState(new WaitForValidActivityState.Builder(RESIZEABLE_ACTIVITY)
489                 .setWindowingMode(windowingMode)
490                 .setActivityType(ACTIVITY_TYPE_STANDARD)
491                 .build());
492         mWmState.assertValidity();
493 
494         // Check if the top activity is now back on primary display.
495         mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
496         mWmState.assertFocusedRootTask(
497                 "Default stack on primary display must be focused after display removed",
498                 windowingMode, ACTIVITY_TYPE_STANDARD);
499         mWmState.assertFocusedActivity(
500                 "Focus must be switched back to activity on primary display",
501                 RESIZEABLE_ACTIVITY);
502     }
503 
504     /**
505      * Tests launching activities on secondary display and then removing it to see if stack focus
506      * is moved correctly.
507      */
508     @Test
testStackFocusSwitchOnStackEmptiedInSleeping()509     public void testStackFocusSwitchOnStackEmptiedInSleeping() {
510         assumeTrue(supportsLockScreen());
511 
512         validateStackFocusSwitchOnStackEmptied(createManagedVirtualDisplaySession(),
513                 createManagedLockScreenSession());
514     }
515 
516     /**
517      * Tests launching activities on secondary display and then finishing it to see if stack focus
518      * is moved correctly.
519      */
520     @Test
testStackFocusSwitchOnStackEmptied()521     public void testStackFocusSwitchOnStackEmptied() {
522         validateStackFocusSwitchOnStackEmptied(createManagedVirtualDisplaySession(),
523                 null /* lockScreenSession */);
524     }
525 
validateStackFocusSwitchOnStackEmptied(VirtualDisplaySession virtualDisplaySession, LockScreenSession lockScreenSession)526     private void validateStackFocusSwitchOnStackEmptied(VirtualDisplaySession virtualDisplaySession,
527             LockScreenSession lockScreenSession) {
528         if (lockScreenSession != null) {
529             lockScreenSession.setLockCredential();
530         }
531 
532         // Create new virtual display.
533         final DisplayContent newDisplay = virtualDisplaySession.createDisplay();
534         mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
535 
536         // Launch activity on new secondary display.
537         launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
538         waitAndAssertActivityStateOnDisplay(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED,
539                 newDisplay.mId,"Top activity must be on secondary display");
540 
541         if (lockScreenSession != null) {
542             // Lock the device, so that activity containers will be detached.
543             lockScreenSession.sleepDevice();
544         }
545 
546         // Finish activity on secondary display.
547         mBroadcastActionTrigger.finishBroadcastReceiverActivity();
548 
549         if (lockScreenSession != null) {
550             // Unlock and check if the focus is switched back to primary display.
551             lockScreenSession.wakeUpDevice().enterAndConfirmLockCredential();
552         }
553 
554         waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, DEFAULT_DISPLAY,
555                 "Top activity must be switched back to primary display");
556     }
557 
558     /**
559      * Tests that input events on the primary display take focus from the virtual display.
560      */
561     @Test
testStackFocusSwitchOnTouchEvent()562     public void testStackFocusSwitchOnTouchEvent() {
563         // If config_perDisplayFocusEnabled, the focus will not move even if touching on
564         // the Activity in the different display.
565         assumeFalse(perDisplayFocusEnabled());
566 
567         // Create new virtual display.
568         final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
569 
570         mWmState.computeState(VIRTUAL_DISPLAY_ACTIVITY);
571         mWmState.assertFocusedActivity("Top activity must be the latest launched one",
572                 VIRTUAL_DISPLAY_ACTIVITY);
573 
574         launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
575 
576         waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
577                 "Activity launched on secondary display must be resumed");
578 
579         // Tap on task center to switch focus between displays. Using task center instead of
580         // display center to cover the multi window scenario.
581         tapOnTaskCenter(mWmState.getTaskByActivity(VIRTUAL_DISPLAY_ACTIVITY));
582 
583         waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, DEFAULT_DISPLAY,
584                 "Top activity must be on the primary display");
585         assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, VIRTUAL_DISPLAY_ACTIVITY),
586                 pair(newDisplay.mId, TEST_ACTIVITY));
587 
588         tapOnDisplayCenter(newDisplay.mId);
589         mWmState.waitForValidState(TEST_ACTIVITY);
590         mWmState.assertFocusedAppOnDisplay("App on secondary display must be focused",
591                 TEST_ACTIVITY, newDisplay.mId);
592     }
593 
594 
595     /**
596      * Tests that tapping on the primary display after showing the keyguard resumes the
597      * activity on the primary display.
598      */
599     @Test
testStackFocusSwitchOnTouchEventAfterKeyguard()600     public void testStackFocusSwitchOnTouchEventAfterKeyguard() {
601         assumeFalse(perDisplayFocusEnabled());
602         assumeTrue(supportsLockScreen());
603 
604         // Launch something on the primary display so we know there is a resumed activity there
605         launchActivity(RESIZEABLE_ACTIVITY);
606         waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
607                 "Activity launched on primary display must be resumed");
608 
609         final LockScreenSession lockScreenSession = createManagedLockScreenSession();
610         lockScreenSession.sleepDevice();
611 
612         // Make sure there is no resumed activity when the primary display is off
613         waitAndAssertActivityState(RESIZEABLE_ACTIVITY, STATE_STOPPED,
614                 "Activity launched on primary display must be stopped after turning off");
615         assertEquals("Unexpected resumed activity",
616                 0, mWmState.getResumedActivitiesCount());
617 
618         final DisplayContent newDisplay = createManagedExternalDisplaySession()
619                 .setCanShowWithInsecureKeyguard(true).createVirtualDisplay();
620 
621         launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
622 
623         // Unlock the device and tap on the middle of the primary display
624         lockScreenSession.wakeUpDevice();
625         executeShellCommand("wm dismiss-keyguard");
626         mWmState.waitForKeyguardGone();
627         mWmState.waitForValidState(RESIZEABLE_ACTIVITY, TEST_ACTIVITY);
628 
629         // Check that the test activity is resumed on the external display and is on top
630         waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
631                 "Activity on external display must be resumed");
632         assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, RESIZEABLE_ACTIVITY),
633                 pair(newDisplay.mId, TEST_ACTIVITY));
634 
635         // Tap on task center to switch focus between displays. Using task center instead of
636         // display center to cover the multi window scenario.
637         tapOnTaskCenter(mWmState.getTaskByActivity(RESIZEABLE_ACTIVITY));
638 
639         // Check that the activity on the primary display is the topmost resumed
640         waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
641                 "Activity on primary display must be resumed and on top");
642         assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, RESIZEABLE_ACTIVITY),
643                 pair(newDisplay.mId, TEST_ACTIVITY));
644     }
645 
646     /**
647      * Tests that showWhenLocked works on a secondary display.
648      */
649     @Test
testSecondaryDisplayShowWhenLocked()650     public void testSecondaryDisplayShowWhenLocked() {
651         assumeTrue(supportsSecureLock());
652 
653         final LockScreenSession lockScreenSession = createManagedLockScreenSession();
654         lockScreenSession.setLockCredential();
655 
656         launchActivity(TEST_ACTIVITY);
657 
658         final DisplayContent newDisplay = createManagedExternalDisplaySession()
659                 .createVirtualDisplay();
660         launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, newDisplay.mId);
661 
662         lockScreenSession.gotoKeyguard();
663 
664         waitAndAssertActivityState(TEST_ACTIVITY, STATE_STOPPED,
665                 "Expected stopped activity on default display");
666         waitAndAssertActivityStateOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, STATE_RESUMED,
667                 newDisplay.mId, "Expected resumed activity on secondary display");
668     }
669 
670     /**
671      * Tests tap and set focus between displays.
672      */
673     @Test
testSecondaryDisplayFocus()674     public void testSecondaryDisplayFocus() {
675         assumeFalse(perDisplayFocusEnabled());
676 
677         launchActivity(TEST_ACTIVITY);
678         mWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
679 
680         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
681                 .setSimulateDisplay(true).createDisplay();
682         launchActivityOnDisplay(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
683         waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId,
684                 "Virtual activity should be Top Resumed Activity.");
685         mWmState.assertFocusedAppOnDisplay("Activity on second display must be focused.",
686                 VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
687 
688         // Tap on task center to switch focus between displays. Using task center instead of
689         // display center to cover the multi window scenario.
690         tapOnTaskCenter(mWmState.getTaskByActivity(TEST_ACTIVITY));
691 
692         waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
693                 "Activity should be top resumed when tapped.");
694         mWmState.assertFocusedActivity("Activity on default display must be top focused.",
695                 TEST_ACTIVITY);
696 
697         tapOnDisplayCenter(newDisplay.mId);
698 
699         waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId,
700                 "Virtual display activity should be top resumed when tapped.");
701         mWmState.assertFocusedActivity("Activity on second display must be top focused.",
702                 VIRTUAL_DISPLAY_ACTIVITY);
703         mWmState.assertFocusedAppOnDisplay(
704                 "Activity on default display must be still focused.",
705                 TEST_ACTIVITY, DEFAULT_DISPLAY);
706     }
707 
708     /**
709      * Tests that toast works on a secondary display.
710      */
711     @Test
testSecondaryDisplayShowToast()712     public void testSecondaryDisplayShowToast() {
713         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
714                 .setPublicDisplay(true)
715                 .createDisplay();
716         final String TOAST_NAME = "Toast";
717         launchActivityOnDisplay(TOAST_ACTIVITY, newDisplay.mId);
718         waitAndAssertActivityStateOnDisplay(TOAST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
719                 "Activity launched on external display must be resumed");
720 
721         assertTrue("Toast window must be shown", mWmState.waitForWithAmState(
722                 state -> state.containsWindow(TOAST_NAME), "toast window to show"));
723         assertTrue("Toast window must be visible",
724                 mWmState.isWindowSurfaceShown(TOAST_NAME));
725     }
726 
727     /**
728      * Tests that the surface size of a fullscreen task is same as its display's surface size.
729      * Also check that the surface size has updated after reparenting to other display.
730      */
731     @Test
testTaskSurfaceSizeAfterReparentDisplay()732     public void testTaskSurfaceSizeAfterReparentDisplay() {
733         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
734             // Create new simulated display and launch an activity on it.
735             final DisplayContent newDisplay = virtualDisplaySession.setSimulateDisplay(true)
736                     .createDisplay();
737             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
738 
739             waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
740                     "Top activity must be the newly launched one");
741             assertTopTaskSameSurfaceSizeWithDisplay(newDisplay.mId);
742 
743             separateTestJournal();
744             // Destroy the display.
745         }
746 
747         // Activity must be reparented to default display and relaunched.
748         assertActivityLifecycle(TEST_ACTIVITY, true /* relaunched */);
749         waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
750                 "Top activity must be reparented to default display");
751 
752         // Check the surface size after task was reparented to default display.
753         assertTopTaskSameSurfaceSizeWithDisplay(DEFAULT_DISPLAY);
754     }
755 
assertTopTaskSameSurfaceSizeWithDisplay(int displayId)756     private void assertTopTaskSameSurfaceSizeWithDisplay(int displayId) {
757         final DisplayContent display = mWmState.getDisplay(displayId);
758         final int stackId = mWmState.getFrontRootTaskId(displayId);
759         final Task task = mWmState.getRootTask(stackId).getTopTask();
760 
761         assertEquals("Task must have same surface width with its display",
762                 display.getSurfaceSize(), task.getSurfaceWidth());
763         assertEquals("Task must have same surface height with its display",
764                 display.getSurfaceSize(), task.getSurfaceHeight());
765     }
766 
767     @Test
testAppTransitionForActivityOnDifferentDisplay()768     public void testAppTransitionForActivityOnDifferentDisplay() {
769         assumeFalse(ENABLE_SHELL_TRANSITIONS);
770         final TestActivitySession<StandardActivity> transitionActivitySession =
771                 createManagedTestActivitySession();
772         // Create new simulated display.
773         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
774                 .setSimulateDisplay(true).createDisplay();
775 
776         // Launch BottomActivity on top of launcher activity to prevent transition state
777         // affected by wallpaper theme.
778         launchActivityOnDisplay(BOTTOM_ACTIVITY, DEFAULT_DISPLAY);
779         waitAndAssertTopResumedActivity(BOTTOM_ACTIVITY, DEFAULT_DISPLAY,
780                 "Activity must be resumed");
781 
782         // Launch StandardActivity on default display, verify last transition if is correct.
783         transitionActivitySession.launchTestActivityOnDisplaySync(StandardActivity.class,
784                 DEFAULT_DISPLAY);
785         mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
786         mWmState.assertValidity();
787         assertEquals(TRANSIT_TASK_OPEN,
788                 mWmState.getDisplay(DEFAULT_DISPLAY).getLastTransition());
789 
790         // Finish current activity & launch another TestActivity in virtual display in parallel.
791         transitionActivitySession.finishCurrentActivityNoWait();
792         launchActivityOnDisplayNoWait(TEST_ACTIVITY, newDisplay.mId);
793         mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
794         mWmState.waitForAppTransitionIdleOnDisplay(newDisplay.mId);
795         mWmState.assertValidity();
796 
797         // Verify each display's last transition if is correct as expected.
798         assertEquals(TRANSIT_TASK_CLOSE,
799                 mWmState.getDisplay(DEFAULT_DISPLAY).getLastTransition());
800         assertEquals(TRANSIT_TASK_OPEN,
801                 mWmState.getDisplay(newDisplay.mId).getLastTransition());
802     }
803 
804     @Test
testNoTransitionWhenMovingActivityToDisplay()805     public void testNoTransitionWhenMovingActivityToDisplay() throws Exception {
806         // Create new simulated display & capture new display's transition state.
807         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
808                 .setSimulateDisplay(true).createDisplay();
809 
810         // Launch TestActivity in virtual display & capture its transition state.
811         launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
812         mWmState.waitForAppTransitionIdleOnDisplay(newDisplay.mId);
813         mWmState.assertValidity();
814         final String lastTranstionOnVirtualDisplay = mWmState
815                 .getDisplay(newDisplay.mId).getLastTransition();
816 
817         // Move TestActivity from virtual display to default display.
818         getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
819                 .allowMultipleInstances(false).setNewTask(true)
820                 .setDisplayId(DEFAULT_DISPLAY).execute();
821 
822         // Verify TestActivity moved to virtual display.
823         waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
824                 "Existing task must be brought to front");
825 
826         // Make sure last transition will not change when task move to another display.
827         assertEquals(lastTranstionOnVirtualDisplay,
828                 mWmState.getDisplay(newDisplay.mId).getLastTransition());
829     }
830 
831     @Test
testPreQTopProcessResumedActivity()832     public void testPreQTopProcessResumedActivity() {
833         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
834                 .setSimulateDisplay(true).createDisplay();
835 
836         getLaunchActivityBuilder().setUseInstrumentation()
837                 .setTargetActivity(SDK_27_TEST_ACTIVITY).setNewTask(true)
838                 .setDisplayId(newDisplay.mId).execute();
839         waitAndAssertTopResumedActivity(SDK_27_TEST_ACTIVITY, newDisplay.mId,
840                 "Activity launched on secondary display must be resumed and focused");
841 
842         getLaunchActivityBuilder().setUseInstrumentation()
843                 .setTargetActivity(SDK_27_LAUNCHING_ACTIVITY).setNewTask(true)
844                 .setDisplayId(DEFAULT_DISPLAY).setWindowingMode(WINDOWING_MODE_FULLSCREEN)
845                 .execute();
846         waitAndAssertTopResumedActivity(SDK_27_LAUNCHING_ACTIVITY, DEFAULT_DISPLAY,
847                 "Activity launched on default display must be resumed and focused");
848 
849         assertEquals("There must be only one resumed activity in the package.", 1,
850                 mWmState.getResumedActivitiesCountInPackage(
851                         SDK_27_LAUNCHING_ACTIVITY.getPackageName()));
852 
853         // Start SeparateProcessActivity in the same task as LaunchingActivity by setting
854         // allowMultipleInstances to false, and the TestActivity should be resumed.
855         getLaunchActivityBuilder().setUseInstrumentation()
856                 .setTargetActivity(SDK_27_SEPARATE_PROCESS_ACTIVITY).setNewTask(true)
857                 .setDisplayId(DEFAULT_DISPLAY).setWindowingMode(WINDOWING_MODE_FULLSCREEN)
858                 .allowMultipleInstances(false).execute();
859         waitAndAssertTopResumedActivity(SDK_27_SEPARATE_PROCESS_ACTIVITY, DEFAULT_DISPLAY,
860                 "Activity launched on default display must be resumed and focused");
861         assertTrue("Activity that was on secondary display must be resumed",
862                 mWmState.hasActivityState(SDK_27_TEST_ACTIVITY, STATE_RESUMED));
863         assertEquals("There must be only two resumed activities in the package.", 2,
864                 mWmState.getResumedActivitiesCountInPackage(
865                         SDK_27_TEST_ACTIVITY.getPackageName()));
866     }
867 
868     @Test
testPreQTopProcessResumedDisplayMoved()869     public void testPreQTopProcessResumedDisplayMoved() throws Exception {
870         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
871                 .setSimulateDisplay(true).createDisplay();
872         getLaunchActivityBuilder().setUseInstrumentation()
873                 .setTargetActivity(SDK_27_LAUNCHING_ACTIVITY).setNewTask(true)
874                 .setDisplayId(DEFAULT_DISPLAY).execute();
875         waitAndAssertTopResumedActivity(SDK_27_LAUNCHING_ACTIVITY, DEFAULT_DISPLAY,
876                 "Activity launched on default display must be resumed and focused");
877 
878         getLaunchActivityBuilder().setUseInstrumentation()
879                 .setTargetActivity(SDK_27_TEST_ACTIVITY).setNewTask(true)
880                 .setDisplayId(newDisplay.mId).execute();
881         // Dismiss DeprecatedTargetSdkVersionDialog to avoid it disturbing tapOnTaskCenter.
882         DeprecatedTargetSdkTest.waitAndDismissDeprecatedTargetSdkDialog(mWmState);
883         waitAndAssertTopResumedActivity(SDK_27_TEST_ACTIVITY, newDisplay.mId,
884                 "Activity launched on secondary display must be resumed and focused");
885 
886         // Tap on task center to switch focus between displays. Using task center instead of
887         // display center to cover the multi window scenario.
888         tapOnTaskCenter(mWmState.getTaskByActivity(SDK_27_LAUNCHING_ACTIVITY));
889         waitAndAssertTopResumedActivity(SDK_27_LAUNCHING_ACTIVITY, DEFAULT_DISPLAY,
890                 "Activity launched on default display must be resumed and focused");
891         assertEquals("There must be only one resumed activity in the package.", 1,
892                 mWmState.getResumedActivitiesCountInPackage(
893                         SDK_27_LAUNCHING_ACTIVITY.getPackageName()));
894     }
895 }
896