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