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