• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package android.server.wm;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_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         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
548              final LockScreenSession lockScreenSession = new LockScreenSession()) {
549             validateStackFocusSwitchOnStackEmptied(virtualDisplaySession, lockScreenSession);
550         }
551     }
552 
553     /**
554      * Tests launching activities on secondary display and then finishing it to see if stack focus
555      * is moved correctly.
556      */
557     @Test
testStackFocusSwitchOnStackEmptied()558     public void testStackFocusSwitchOnStackEmptied() throws Exception {
559         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
560             validateStackFocusSwitchOnStackEmptied(virtualDisplaySession,
561                     null /* lockScreenSession */);
562         }
563     }
564 
validateStackFocusSwitchOnStackEmptied(VirtualDisplaySession virtualDisplaySession, LockScreenSession lockScreenSession)565     private void validateStackFocusSwitchOnStackEmptied(VirtualDisplaySession virtualDisplaySession,
566             LockScreenSession lockScreenSession) throws Exception {
567         // Create new virtual display.
568         final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
569         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
570         final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
571 
572         // Launch activity on new secondary display.
573         launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
574         waitAndAssertTopResumedActivity(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId,
575                 "Top activity must be on secondary display");
576 
577         if (lockScreenSession != null) {
578             // Lock the device, so that activity containers will be detached.
579             lockScreenSession.sleepDevice();
580         }
581 
582         // Finish activity on secondary display.
583         mBroadcastActionTrigger.finishBroadcastReceiverActivity();
584 
585         if (lockScreenSession != null) {
586             // Unlock and check if the focus is switched back to primary display.
587             lockScreenSession.wakeUpDevice().unlockDevice();
588         }
589 
590         waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, DEFAULT_DISPLAY,
591                 "Top activity must be switched back to primary display");
592     }
593 
594     /**
595      * Tests that input events on the primary display take focus from the virtual display.
596      */
597     @Test
testStackFocusSwitchOnTouchEvent()598     public void testStackFocusSwitchOnTouchEvent() throws Exception {
599         // If config_perDisplayFocusEnabled, the focus will not move even if touching on
600         // the Activity in the different display.
601         assumeFalse(perDisplayFocusEnabled());
602 
603         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
604             // Create new virtual display.
605             final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
606 
607             mAmWmState.computeState(VIRTUAL_DISPLAY_ACTIVITY);
608             mAmWmState.assertFocusedActivity("Top activity must be the latest launched one",
609                     VIRTUAL_DISPLAY_ACTIVITY);
610 
611             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
612 
613             waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
614                     "Activity launched on secondary display must be on top");
615 
616             tapOnDisplayCenter(DEFAULT_DISPLAY);
617 
618             waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, DEFAULT_DISPLAY,
619                     "Top activity must be on the primary display");
620             mAmWmState.assertResumedActivities("Both displays must have resumed activities",
621                     new SparseArray<ComponentName>(){{
622                         put(DEFAULT_DISPLAY, VIRTUAL_DISPLAY_ACTIVITY);
623                         put(newDisplay.mId, TEST_ACTIVITY);
624                     }}
625             );
626             mAmWmState.assertFocusedAppOnDisplay("App on secondary display must still be focused",
627                     TEST_ACTIVITY, newDisplay.mId);
628         }
629     }
630 
631 
632     /**
633      * Tests that tapping on the primary display after showing the keyguard resumes the
634      * activity on the primary display.
635      */
636     @Test
testStackFocusSwitchOnTouchEventAfterKeyguard()637     public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
638         assumeFalse(perDisplayFocusEnabled());
639 
640         // Launch something on the primary display so we know there is a resumed activity there
641         launchActivity(RESIZEABLE_ACTIVITY);
642         waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
643                 "Activity launched on primary display must be resumed");
644 
645         try (final LockScreenSession lockScreenSession = new LockScreenSession();
646              final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
647             lockScreenSession.sleepDevice();
648 
649             // Make sure there is no resumed activity when the primary display is off
650             waitAndAssertActivityState(RESIZEABLE_ACTIVITY, STATE_STOPPED,
651                     "Activity launched on primary display must be stopped after turning off");
652             assertEquals("Unexpected resumed activity",
653                     0, mAmWmState.getAmState().getResumedActivitiesCount());
654 
655             final ActivityDisplay newDisplay = externalDisplaySession
656                     .setCanShowWithInsecureKeyguard(true).createVirtualDisplay();
657 
658             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
659 
660             // Unlock the device and tap on the middle of the primary display
661             lockScreenSession.wakeUpDevice();
662             executeShellCommand("wm dismiss-keyguard");
663             mAmWmState.waitForKeyguardGone();
664             mAmWmState.waitForValidState(RESIZEABLE_ACTIVITY, TEST_ACTIVITY);
665 
666             // Check that the test activity is resumed on the external display and is on top
667             waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
668                     "Activity on external display must be resumed and on top");
669             mAmWmState.assertResumedActivities("Both displays should have resumed activities",
670                     new SparseArray<ComponentName>(){{
671                         put(DEFAULT_DISPLAY, RESIZEABLE_ACTIVITY);
672                         put(newDisplay.mId, TEST_ACTIVITY);
673                     }}
674             );
675 
676             tapOnDisplayCenter(DEFAULT_DISPLAY);
677 
678             // Check that the activity on the primary display is the topmost resumed
679             waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
680                     "Activity on primary display must be resumed and on top");
681             mAmWmState.assertResumedActivities("Both displays should have resumed activities",
682                     new SparseArray<ComponentName>(){{
683                         put(DEFAULT_DISPLAY, RESIZEABLE_ACTIVITY);
684                         put(newDisplay.mId, TEST_ACTIVITY);
685                     }}
686             );
687             mAmWmState.assertFocusedAppOnDisplay("App on external display must still be focused",
688                     TEST_ACTIVITY, newDisplay.mId);
689         }
690     }
691 
692     /**
693      * Tests that showWhenLocked works on a secondary display.
694      */
695     @Test
testSecondaryDisplayShowWhenLocked()696     public void testSecondaryDisplayShowWhenLocked() throws Exception {
697         assumeTrue(supportsSecureLock());
698 
699         try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
700              final LockScreenSession lockScreenSession = new LockScreenSession()) {
701             lockScreenSession.setLockCredential();
702 
703             launchActivity(TEST_ACTIVITY);
704 
705             final ActivityDisplay newDisplay = externalDisplaySession.createVirtualDisplay();
706             launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, newDisplay.mId);
707 
708             lockScreenSession.gotoKeyguard();
709 
710             waitAndAssertActivityState(TEST_ACTIVITY, STATE_STOPPED,
711                     "Expected stopped activity on default display");
712             waitAndAssertTopResumedActivity(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, newDisplay.mId,
713                     "Expected resumed activity on secondary display");
714         }
715     }
716 
717     /**
718      * Tests tap and set focus between displays.
719      */
720     @Test
testSecondaryDisplayFocus()721     public void testSecondaryDisplayFocus() throws Exception {
722         assumeFalse(perDisplayFocusEnabled());
723 
724         try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
725             launchActivity(TEST_ACTIVITY);
726             mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
727 
728             final ActivityDisplay newDisplay = externalDisplaySession.createVirtualDisplay();
729             launchActivityOnDisplay(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
730             waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId,
731                     "Virtual activity should be Top Resumed Activity.");
732             mAmWmState.assertFocusedAppOnDisplay("Activity on second display must be focused.",
733                     VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
734 
735             tapOnDisplayCenter(DEFAULT_DISPLAY);
736 
737             waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
738                     "Activity should be top resumed when tapped.");
739             mAmWmState.assertFocusedActivity("Activity on default display must be top focused.",
740                     TEST_ACTIVITY);
741 
742             tapOnDisplayCenter(newDisplay.mId);
743 
744             waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId,
745                     "Virtual display activity should be top resumed when tapped.");
746             mAmWmState.assertFocusedActivity("Activity on second display must be top focused.",
747                     VIRTUAL_DISPLAY_ACTIVITY);
748             mAmWmState.assertFocusedAppOnDisplay(
749                     "Activity on default display must be still focused.",
750                     TEST_ACTIVITY, DEFAULT_DISPLAY);
751         }
752     }
753 
754     /**
755      * Tests that toast works on a secondary display.
756      */
757     @Test
758     @FlakyTest(bugId = 131005232)
testSecondaryDisplayShowToast()759     public void testSecondaryDisplayShowToast() throws Exception {
760         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
761             final ActivityDisplay newDisplay =
762                     virtualDisplaySession.setPublicDisplay(true).createDisplay();
763             final String TOAST_NAME = "Toast";
764             launchActivityOnDisplay(TOAST_ACTIVITY, newDisplay.mId);
765             waitAndAssertTopResumedActivity(TOAST_ACTIVITY, newDisplay.mId,
766                     "Activity launched on external display must be resumed");
767             mAmWmState.waitForWithWmState((state) -> state.containsWindow(TOAST_NAME),
768                     "Waiting for toast window to show");
769 
770             assertTrue("Toast window must be shown",
771                     mAmWmState.getWmState().containsWindow(TOAST_NAME));
772             assertTrue("Toast window must be visible",
773                     mAmWmState.getWmState().isWindowVisible(TOAST_NAME));
774         }
775     }
776 
777     /**
778      * Tests that the surface size of a fullscreen task is same as its display's surface size.
779      * Also check that the surface size has updated after reparenting to other display.
780      */
781     @Test
testTaskSurfaceSizeAfterReparentDisplay()782     public void testTaskSurfaceSizeAfterReparentDisplay() throws Exception {
783         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
784             // Create new simulated display and launch an activity on it.
785             final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
786                     .createDisplay();
787             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
788 
789             waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
790                     "Top activity must be the newly launched one");
791             assertTopTaskSameSurfaceSizeWithDisplay(newDisplay.mId);
792 
793             separateTestJournal();
794             // Destroy the display.
795         }
796 
797         // Activity must be reparented to default display and relaunched.
798         assertActivityLifecycle(TEST_ACTIVITY, true /* relaunched */);
799         waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
800                 "Top activity must be reparented to default display");
801 
802         // Check the surface size after task was reparented to default display.
803         assertTopTaskSameSurfaceSizeWithDisplay(DEFAULT_DISPLAY);
804     }
805 
assertTopTaskSameSurfaceSizeWithDisplay(int displayId)806     private void assertTopTaskSameSurfaceSizeWithDisplay(int displayId) {
807         final WindowManagerState.Display display = mAmWmState.getWmState().getDisplay(displayId);
808         final int stackId = mAmWmState.getWmState().getFrontStackId(displayId);
809         final WindowManagerState.WindowTask task =
810                 mAmWmState.getWmState().getStack(stackId).mTasks.get(0);
811 
812         assertEquals("Task must have same surface width with its display",
813                 display.getSurfaceSize(), task.getSurfaceWidth());
814         assertEquals("Task must have same surface height with its display",
815                 display.getSurfaceSize(), task.getSurfaceHeight());
816     }
817 
818     @Test
testAppTransitionForActivityOnDifferentDisplay()819     public void testAppTransitionForActivityOnDifferentDisplay() throws Exception {
820         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
821              final TestActivitySession<StandardActivity> transitionActivitySession = new
822                      TestActivitySession<>()) {
823             // Create new simulated display.
824             final ActivityDisplay newDisplay = virtualDisplaySession
825                     .setSimulateDisplay(true).createDisplay();
826 
827             // Launch BottomActivity on top of launcher activity to prevent transition state
828             // affected by wallpaper theme.
829             launchActivityOnDisplay(BOTTOM_ACTIVITY, DEFAULT_DISPLAY);
830             waitAndAssertTopResumedActivity(BOTTOM_ACTIVITY, DEFAULT_DISPLAY,
831                     "Activity must be resumed");
832 
833             // Launch StandardActivity on default display, verify last transition if is correct.
834             transitionActivitySession.launchTestActivityOnDisplaySync(StandardActivity.class,
835                     DEFAULT_DISPLAY);
836             mAmWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
837             mAmWmState.assertSanity();
838             assertEquals(TRANSIT_TASK_OPEN,
839                     mAmWmState.getWmState().getDisplay(DEFAULT_DISPLAY).getLastTransition());
840 
841             // Finish current activity & launch another TestActivity in virtual display in parallel.
842             transitionActivitySession.finishCurrentActivityNoWait();
843             launchActivityOnDisplayNoWait(TEST_ACTIVITY, newDisplay.mId);
844             mAmWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
845             mAmWmState.waitForAppTransitionIdleOnDisplay(newDisplay.mId);
846             mAmWmState.assertSanity();
847 
848             // Verify each display's last transition if is correct as expected.
849             assertEquals(TRANSIT_TASK_CLOSE,
850                     mAmWmState.getWmState().getDisplay(DEFAULT_DISPLAY).getLastTransition());
851             assertEquals(TRANSIT_TASK_OPEN,
852                     mAmWmState.getWmState().getDisplay(newDisplay.mId).getLastTransition());
853         }
854     }
855 
856     @Test
testNoTransitionWhenMovingActivityToDisplay()857     public void testNoTransitionWhenMovingActivityToDisplay() throws Exception {
858         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
859             // Create new simulated display & capture new display's transition state.
860             final ActivityDisplay newDisplay = virtualDisplaySession
861                     .setSimulateDisplay(true).createDisplay();
862 
863             // Launch TestActivity in virtual display & capture its transition state.
864             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
865             mAmWmState.waitForAppTransitionIdleOnDisplay(newDisplay.mId);
866             mAmWmState.assertSanity();
867             final String lastTranstionOnVirtualDisplay = mAmWmState.getWmState()
868                     .getDisplay(newDisplay.mId).getLastTransition();
869 
870             // Move TestActivity from virtual display to default display.
871             getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
872                     .allowMultipleInstances(false).setNewTask(true)
873                     .setDisplayId(DEFAULT_DISPLAY).execute();
874 
875             // Verify TestActivity moved to virtual display.
876             waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
877                     "Existing task must be brought to front");
878 
879             // Make sure last transition will not change when task move to another display.
880             assertEquals(lastTranstionOnVirtualDisplay,
881                     mAmWmState.getWmState().getDisplay(newDisplay.mId).getLastTransition());
882         }
883     }
884 
885     @Test
testPreQTopProcessResumedActivity()886     public void testPreQTopProcessResumedActivity() throws Exception {
887         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
888             final ActivityDisplay newDisplay =
889                     virtualDisplaySession.setSimulateDisplay(true).createDisplay();
890 
891             getLaunchActivityBuilder().setUseInstrumentation()
892                     .setTargetActivity(SDK_27_TEST_ACTIVITY).setNewTask(true)
893                     .setDisplayId(newDisplay.mId).execute();
894             waitAndAssertTopResumedActivity(SDK_27_TEST_ACTIVITY, newDisplay.mId,
895                     "Activity launched on secondary display must be resumed and focused");
896 
897             getLaunchActivityBuilder().setUseInstrumentation()
898                     .setTargetActivity(SDK_27_LAUNCHING_ACTIVITY).setNewTask(true)
899                     .setDisplayId(DEFAULT_DISPLAY).execute();
900             waitAndAssertTopResumedActivity(SDK_27_LAUNCHING_ACTIVITY, DEFAULT_DISPLAY,
901                     "Activity launched on default display must be resumed and focused");
902 
903             assertEquals("There must be only one resumed activity in the package.", 1,
904                     mAmWmState.getAmState().getResumedActivitiesCountInPackage(
905                             SDK_27_LAUNCHING_ACTIVITY.getPackageName()));
906 
907             getLaunchActivityBuilder().setUseInstrumentation()
908                     .setTargetActivity(SDK_27_SEPARATE_PROCESS_ACTIVITY).setNewTask(true)
909                     .setDisplayId(DEFAULT_DISPLAY).execute();
910             waitAndAssertTopResumedActivity(SDK_27_SEPARATE_PROCESS_ACTIVITY, DEFAULT_DISPLAY,
911                     "Activity launched on default display must be resumed and focused");
912             assertTrue("Activity that was on secondary display must be resumed",
913                     mAmWmState.getAmState().hasActivityState(SDK_27_TEST_ACTIVITY, STATE_RESUMED));
914             assertEquals("There must be only two resumed activities in the package.", 2,
915                     mAmWmState.getAmState().getResumedActivitiesCountInPackage(
916                             SDK_27_TEST_ACTIVITY.getPackageName()));
917         }
918     }
919 
920 }
921