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