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_ASSISTANT;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
23 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
24 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
25 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
26 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
27 import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
28 import static android.server.wm.ActivityLauncher.KEY_ACTION;
29 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
30 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_IMPLICIT;
31 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_PENDING;
32 import static android.server.wm.ActivityLauncher.KEY_NEW_TASK;
33 import static android.server.wm.ActivityLauncher.KEY_USE_APPLICATION_CONTEXT;
34 import static android.server.wm.CliIntentExtra.extraBool;
35 import static android.server.wm.CliIntentExtra.extraString;
36 import static android.server.wm.ComponentNameUtils.getActivityName;
37 import static android.server.wm.ShellCommandHelper.executeShellCommand;
38 import static android.server.wm.UiDeviceUtils.pressHomeButton;
39 import static android.server.wm.WindowManagerState.STATE_DESTROYED;
40 import static android.server.wm.WindowManagerState.STATE_RESUMED;
41 import static android.server.wm.WindowManagerState.STATE_STOPPED;
42 import static android.server.wm.app.Components.ALT_LAUNCHING_ACTIVITY;
43 import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
44 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
45 import static android.server.wm.app.Components.NON_RESIZEABLE_ACTIVITY;
46 import static android.server.wm.app.Components.NO_HISTORY_ACTIVITY;
47 import static android.server.wm.app.Components.NO_HISTORY_ACTIVITY2;
48 import static android.server.wm.app.Components.RESIZEABLE_ACTIVITY;
49 import static android.server.wm.app.Components.SHOW_WHEN_LOCKED_ACTIVITY;
50 import static android.server.wm.app.Components.SINGLE_TOP_ACTIVITY;
51 import static android.server.wm.app.Components.TEST_ACTIVITY;
52 import static android.server.wm.app.Components.TOP_ACTIVITY;
53 import static android.server.wm.app.Components.VIRTUAL_DISPLAY_ACTIVITY;
54 import static android.server.wm.second.Components.IMPLICIT_TARGET_SECOND_ACTIVITY;
55 import static android.server.wm.second.Components.IMPLICIT_TARGET_SECOND_TEST_ACTION;
56 import static android.server.wm.second.Components.SECOND_ACTIVITY;
57 import static android.server.wm.second.Components.SECOND_LAUNCH_BROADCAST_ACTION;
58 import static android.server.wm.second.Components.SECOND_LAUNCH_BROADCAST_RECEIVER;
59 import static android.server.wm.third.Components.THIRD_ACTIVITY;
60 import static android.view.Display.DEFAULT_DISPLAY;
61 
62 import static org.junit.Assert.assertEquals;
63 import static org.junit.Assert.assertFalse;
64 import static org.junit.Assert.assertNotEquals;
65 import static org.junit.Assert.assertTrue;
66 import static org.junit.Assume.assumeTrue;
67 
68 import android.app.Activity;
69 import android.app.ActivityOptions;
70 import android.app.PendingIntent;
71 import android.content.ComponentName;
72 import android.content.Context;
73 import android.content.Intent;
74 import android.content.res.Configuration;
75 import android.hardware.display.DisplayManager;
76 import android.hardware.display.VirtualDisplay;
77 import android.os.Bundle;
78 import android.platform.test.annotations.Presubmit;
79 import android.server.wm.CommandSession.ActivitySession;
80 import android.server.wm.CommandSession.SizeInfo;
81 import android.server.wm.MultiDisplayTestBase;
82 import android.server.wm.WindowManagerState.DisplayContent;
83 import android.server.wm.WindowManagerState.Task;
84 import android.view.SurfaceView;
85 
86 import org.junit.Before;
87 import org.junit.Test;
88 
89 /**
90  * Build/Install/Run:
91  *     atest CtsWindowManagerDeviceMultiDisplay:MultiDisplayActivityLaunchTests
92  *
93  *  Tests activity launching behavior on multi-display environment.
94  */
95 @Presubmit
96 @android.server.wm.annotation.Group3
97 public class MultiDisplayActivityLaunchTests extends MultiDisplayTestBase {
98 
99     @Before
100     @Override
setUp()101     public void setUp() throws Exception {
102         super.setUp();
103         assumeTrue(supportsMultiDisplay());
104     }
105 
106     /**
107      * Tests launching an activity on virtual display.
108      */
109     @Test
testLaunchActivityOnSecondaryDisplay()110     public void testLaunchActivityOnSecondaryDisplay() throws Exception {
111         validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_STANDARD);
112     }
113 
114     /**
115      * Tests launching a recent activity on virtual display.
116      */
117     @Test
testLaunchRecentActivityOnSecondaryDisplay()118     public void testLaunchRecentActivityOnSecondaryDisplay() throws Exception {
119         validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_RECENTS);
120     }
121 
122     /**
123      * Tests launching an assistant activity on virtual display.
124      */
125     @Test
testLaunchAssistantActivityOnSecondaryDisplay()126     public void testLaunchAssistantActivityOnSecondaryDisplay() {
127         validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_ASSISTANT);
128     }
129 
validateActivityLaunchOnNewDisplay(int activityType)130     private void validateActivityLaunchOnNewDisplay(int activityType) {
131         // Create new virtual display.
132         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
133                 .setSimulateDisplay(true).createDisplay();
134 
135         // Launch activity on new secondary display.
136         separateTestJournal();
137         getLaunchActivityBuilder().setUseInstrumentation().setWithShellPermission(true)
138                 .setTargetActivity(TEST_ACTIVITY).setNewTask(true)
139                 .setMultipleTask(true).setActivityType(activityType)
140                 .setDisplayId(newDisplay.mId).execute();
141         waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
142                 "Activity launched on secondary display must be focused and on top");
143 
144         // Check that activity config corresponds to display config.
145         final SizeInfo reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY);
146         assertEquals("Activity launched on secondary display must have proper configuration",
147                 CUSTOM_DENSITY_DPI, reportedSizes.densityDpi);
148 
149         assertEquals("Top activity must have correct activity type", activityType,
150                 mWmState.getFrontRootTaskActivityType(newDisplay.mId));
151     }
152 
153     /**
154      * Tests launching an activity on primary display explicitly.
155      */
156     @Test
testLaunchActivityOnPrimaryDisplay()157     public void testLaunchActivityOnPrimaryDisplay() throws Exception {
158         // Launch activity on primary display explicitly.
159         launchActivityOnDisplay(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY);
160 
161         waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY,
162                 "Activity launched on primary display must be focused and on top");
163 
164         // Launch another activity on primary display using the first one
165         getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).setNewTask(true)
166                 .setMultipleTask(true).setDisplayId(DEFAULT_DISPLAY).execute();
167         mWmState.computeState(TEST_ACTIVITY);
168 
169         waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
170                 "Activity launched on primary display must be focused");
171     }
172 
173     /**
174      * Tests launching an existing activity from an activity that resides on secondary display. An
175      * existing activity on a different display should be moved to the display of the launching
176      * activity.
177      */
178     @Test
testLaunchActivityFromSecondaryDisplay()179     public void testLaunchActivityFromSecondaryDisplay() {
180         getLaunchActivityBuilder().setUseInstrumentation()
181                 .setTargetActivity(TEST_ACTIVITY).setNewTask(true)
182                 .setDisplayId(DEFAULT_DISPLAY).execute();
183 
184         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
185                 .setSimulateDisplay(true)
186                 .createDisplay();
187         final int newDisplayId = newDisplay.mId;
188 
189         getLaunchActivityBuilder().setUseInstrumentation()
190                 .setTargetActivity(BROADCAST_RECEIVER_ACTIVITY).setNewTask(true)
191                 .setDisplayId(newDisplayId).execute();
192         waitAndAssertTopResumedActivity(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId,
193                 "Activity should be resumed on secondary display");
194 
195         mayLaunchHomeActivityForCar();
196         mBroadcastActionTrigger.launchActivityNewTask(getActivityName(TEST_ACTIVITY));
197         waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplayId,
198                 "Activity should be resumed on secondary display");
199 
200         getLaunchActivityBuilder().setUseInstrumentation()
201                 .setTargetActivity(TEST_ACTIVITY).setNewTask(true)
202                 .setDisplayId(DEFAULT_DISPLAY).execute();
203         waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
204                 "Activity should be the top resumed on default display");
205     }
206 
207     /**
208      * Tests that an activity can be launched on a secondary display while the primary
209      * display is off.
210      */
211     @Test
testLaunchExternalDisplayActivityWhilePrimaryOff()212     public void testLaunchExternalDisplayActivityWhilePrimaryOff() {
213         // Leanback devices may launch a live broadcast app during screen off-on cycles.
214         final boolean mayLaunchActivityOnScreenOff = isLeanBack();
215 
216         // Launch something on the primary display so we know there is a resumed activity there
217         launchActivity(RESIZEABLE_ACTIVITY);
218         waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
219                 "Activity launched on primary display must be resumed");
220 
221         final PrimaryDisplayStateSession displayStateSession =
222                 mObjectTracker.manage(new PrimaryDisplayStateSession());
223         final ExternalDisplaySession externalDisplaySession = createManagedExternalDisplaySession();
224         displayStateSession.turnScreenOff();
225 
226         // Make sure there is no resumed activity when the primary display is off
227         if (!mayLaunchActivityOnScreenOff) {
228             waitAndAssertActivityState(RESIZEABLE_ACTIVITY, STATE_STOPPED,
229                     "Activity launched on primary display must be stopped after turning off");
230             assertEquals("Unexpected resumed activity",
231                     0, mWmState.getResumedActivitiesCount());
232         }
233 
234         final DisplayContent newDisplay = externalDisplaySession
235                 .setCanShowWithInsecureKeyguard(true).createVirtualDisplay();
236 
237         launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
238 
239         // Check that the test activity is resumed on the external display
240         waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
241                 "Activity launched on external display must be resumed");
242         if (!mayLaunchActivityOnScreenOff) {
243             mWmState.assertFocusedAppOnDisplay("App on default display must still be focused",
244                     RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY);
245         }
246     }
247 
248     /**
249      * Tests launching a non-resizeable activity on virtual display. It should land on the
250      * virtual display with correct configuration.
251      */
252     @Test
testLaunchNonResizeableActivityOnSecondaryDisplay()253     public void testLaunchNonResizeableActivityOnSecondaryDisplay() {
254         // Create new virtual display.
255         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
256                 .setSimulateDisplay(true).createDisplay();
257 
258         // Launch activity on new secondary display.
259         launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN, newDisplay.mId);
260 
261         waitAndAssertTopResumedActivity(NON_RESIZEABLE_ACTIVITY, newDisplay.mId,
262                 "Activity requested to launch on secondary display must be focused");
263 
264         final Configuration taskConfig = mWmState
265                 .getTaskByActivity(NON_RESIZEABLE_ACTIVITY).getFullConfiguration();
266         final Configuration displayConfig = mWmState
267                 .getDisplay(newDisplay.mId).getFullConfiguration();
268 
269         // Check that activity config corresponds to display config.
270         assertEquals("Activity launched on secondary display must have proper configuration",
271                 taskConfig.densityDpi, displayConfig.densityDpi);
272 
273         assertEquals("Activity launched on secondary display must have proper configuration",
274                 taskConfig.windowConfiguration.getBounds(),
275                 displayConfig.windowConfiguration.getBounds());
276     }
277 
278     /**
279      * Tests successfully moving a non-resizeable activity to a virtual display.
280      */
281     @Test
testMoveNonResizeableActivityToSecondaryDisplay()282     public void testMoveNonResizeableActivityToSecondaryDisplay() {
283         final VirtualDisplayLauncher virtualLauncher =
284                 mObjectTracker.manage(new VirtualDisplayLauncher());
285         // Create new virtual display.
286         final DisplayContent newDisplay = virtualLauncher
287                 .setSimulateDisplay(true).createDisplay();
288         // Launch a non-resizeable activity on a primary display.
289         final ActivitySession nonResizeableSession = virtualLauncher.launchActivity(
290                 builder -> builder.setTargetActivity(NON_RESIZEABLE_ACTIVITY).setNewTask(true));
291 
292         // Launch a resizeable activity on new secondary display to create a new task there.
293         virtualLauncher.launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay);
294         final int externalFrontRootTaskId = mWmState
295                 .getFrontRootTaskId(newDisplay.mId);
296 
297         // Clear lifecycle callback history before moving the activity so the later verification
298         // can get the callbacks which are related to the reparenting.
299         nonResizeableSession.takeCallbackHistory();
300 
301         mayLaunchHomeActivityForCar();
302         // Try to move the non-resizeable activity to the top of the root task on secondary display.
303         moveActivityToRootTaskOrOnTop(NON_RESIZEABLE_ACTIVITY, externalFrontRootTaskId);
304         // Wait for a while to check that it will move.
305         assertTrue("Non-resizeable activity should be moved",
306                 mWmState.waitForWithAmState(
307                         state -> newDisplay.mId == state
308                                 .getDisplayByActivity(NON_RESIZEABLE_ACTIVITY),
309                         "seeing if activity won't be moved"));
310 
311         waitAndAssertTopResumedActivity(NON_RESIZEABLE_ACTIVITY, newDisplay.mId,
312                 "The moved non-resizeable activity must be focused");
313         assertActivityLifecycle(nonResizeableSession, true /* relaunched */);
314     }
315 
316     /**
317      * Tests launching a non-resizeable activity on virtual display from activity there. It should
318      * land on the secondary display based on the resizeability of the root activity of the task.
319      */
320     @Test
testLaunchNonResizeableActivityFromSecondaryDisplaySameTask()321     public void testLaunchNonResizeableActivityFromSecondaryDisplaySameTask() {
322         // Create new simulated display.
323         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
324                 .setSimulateDisplay(true)
325                 .createDisplay();
326 
327         // Launch activity on new secondary display.
328         launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
329         waitAndAssertTopResumedActivity(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId,
330                 "Activity launched on secondary display must be focused");
331 
332         // Launch non-resizeable activity from secondary display.
333         mBroadcastActionTrigger.launchActivityNewTask(getActivityName(NON_RESIZEABLE_ACTIVITY));
334         waitAndAssertTopResumedActivity(NON_RESIZEABLE_ACTIVITY, newDisplay.mId,
335                 "Launched activity must be on the secondary display and resumed");
336     }
337 
338     /**
339      * Tests launching a non-resizeable activity on virtual display in a new task from activity
340      * there. It must land on the display as its caller.
341      */
342     @Test
testLaunchNonResizeableActivityFromSecondaryDisplayNewTask()343     public void testLaunchNonResizeableActivityFromSecondaryDisplayNewTask() {
344         // Create new virtual display.
345         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
346                 .setSimulateDisplay(true).createDisplay();
347 
348         // Launch activity on new secondary display.
349         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
350         waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
351                 "Activity launched on secondary display must be focused");
352 
353         // Launch non-resizeable activity from secondary display in a new task.
354         getLaunchActivityBuilder().setTargetActivity(NON_RESIZEABLE_ACTIVITY)
355                 .setNewTask(true).setMultipleTask(true).execute();
356 
357         mWmState.waitForActivityState(NON_RESIZEABLE_ACTIVITY, STATE_RESUMED);
358 
359         // Check that non-resizeable activity is on the same display.
360         final int newFrontRootTaskId = mWmState.getFocusedTaskId();
361         final Task newFrontRootTask = mWmState.getRootTask(newFrontRootTaskId);
362         assertEquals("Launched activity must be on the same display", newDisplay.mId,
363                 newFrontRootTask.mDisplayId);
364         assertEquals("Launched activity must be resumed",
365                 getActivityName(NON_RESIZEABLE_ACTIVITY),
366                 newFrontRootTask.getResumedActivity());
367         mWmState.assertFocusedRootTask(
368                 "Top task must be the one with just launched activity",
369                 newFrontRootTaskId);
370         mWmState.assertResumedActivity("NON_RESIZEABLE_ACTIVITY not resumed",
371                 NON_RESIZEABLE_ACTIVITY);
372     }
373 
374     /**
375      * Tests launching an activity on virtual display and then launching another activity
376      * via shell command and without specifying the display id - the second activity
377      * must appear on the same display due to process affinity.
378      */
379     @Test
testConsequentLaunchActivity()380     public void testConsequentLaunchActivity() {
381         // Create new virtual display.
382         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
383                 .setSimulateDisplay(true).createDisplay();
384 
385         // Launch activity on new secondary display.
386         launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
387 
388         waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
389                 "Activity launched on secondary display must be on top");
390 
391         // Launch second activity without specifying display.
392         launchActivity(LAUNCHING_ACTIVITY);
393 
394         // Check that activity is launched in focused task on the new display.
395         waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
396                 "Launched activity must be focused");
397         mWmState.assertResumedActivity("LAUNCHING_ACTIVITY must be resumed", LAUNCHING_ACTIVITY);
398     }
399 
400     /**
401      * Tests launching an activity on a virtual display and then launching another activity in
402      * a new process via shell command and without specifying the display id - the second activity
403      * must appear on the primary display.
404      */
405     @Test
testConsequentLaunchActivityInNewProcess()406     public void testConsequentLaunchActivityInNewProcess() {
407         // Create new virtual display.
408         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
409                 .setSimulateDisplay(true).createDisplay();
410 
411         // Launch activity on new secondary display.
412         launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
413 
414         waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
415                 "Activity launched on secondary display must be on top");
416 
417         // Launch second activity without specifying display.
418         launchActivity(SECOND_ACTIVITY);
419 
420         // Check that activity is launched in focused task on primary display.
421         waitAndAssertTopResumedActivity(SECOND_ACTIVITY, DEFAULT_DISPLAY,
422                 "Launched activity must be focused");
423         assertBothDisplaysHaveResumedActivities(pair(newDisplay.mId, TEST_ACTIVITY),
424                 pair(DEFAULT_DISPLAY, SECOND_ACTIVITY));
425     }
426 
427     /**
428      * Tests launching an activity on simulated display and then launching another activity from the
429      * first one - it must appear on the secondary display, because it was launched from there.
430      */
431     @Test
testConsequentLaunchActivityFromSecondaryDisplay()432     public void testConsequentLaunchActivityFromSecondaryDisplay() {
433         // Create new simulated display.
434         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
435                 .setSimulateDisplay(true)
436                 .createDisplay();
437 
438         // Launch activity on new secondary display.
439         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
440 
441         waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
442                 "Activity launched on secondary display must be on top");
443 
444         // Launch second activity from app on secondary display without specifying display id.
445         getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute();
446 
447         // Check that activity is launched in focused task on external display.
448         waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
449                 "Launched activity must be on top");
450     }
451 
452     /**
453      * Tests launching an activity on virtual display and then launching another activity from the
454      * first one - it must appear on the secondary display, because it was launched from there.
455      */
456     @Test
testConsequentLaunchActivityFromVirtualDisplay()457     public void testConsequentLaunchActivityFromVirtualDisplay() {
458         // Create new virtual display.
459         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
460                 .setSimulateDisplay(true).createDisplay();
461 
462         // Launch activity on new secondary display.
463         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
464 
465         waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
466                 "Activity launched on secondary display must be on top");
467 
468         // Launch second activity from app on secondary display without specifying display id.
469         getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute();
470         mWmState.computeState(TEST_ACTIVITY);
471 
472         // Check that activity is launched in focused task on external display.
473         waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
474                 "Launched activity must be on top");
475     }
476 
477     /**
478      * Tests launching an activity on virtual display and then launching another activity from the
479      * first one with specifying the target display - it must appear on the secondary display.
480      */
481     @Test
testConsequentLaunchActivityFromVirtualDisplayToTargetDisplay()482     public void testConsequentLaunchActivityFromVirtualDisplayToTargetDisplay() {
483         // Create new virtual display.
484         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
485                 .setSimulateDisplay(true).createDisplay();
486 
487         // Launch activity on new secondary display.
488         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
489 
490         waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
491                 "Activity launched on secondary display must be on top");
492 
493         // Launch second activity from app on secondary display specifying same display id.
494         getLaunchActivityBuilder()
495                 .setTargetActivity(SECOND_ACTIVITY)
496                 .setDisplayId(newDisplay.mId)
497                 .execute();
498 
499         // Check that activity is launched in focused task on external display.
500         waitAndAssertTopResumedActivity(SECOND_ACTIVITY, newDisplay.mId,
501                 "Launched activity must be on top");
502 
503         // Launch other activity with different uid and check if it has launched successfully.
504         getLaunchActivityBuilder()
505                 .setUseBroadcastReceiver(SECOND_LAUNCH_BROADCAST_RECEIVER,
506                         SECOND_LAUNCH_BROADCAST_ACTION)
507                 .setDisplayId(newDisplay.mId)
508                 .setTargetActivity(THIRD_ACTIVITY)
509                 .execute();
510 
511         // Check that activity is launched in focused task on external display.
512         waitAndAssertTopResumedActivity(THIRD_ACTIVITY, newDisplay.mId,
513                 "Launched activity must be on top");
514     }
515 
516     /**
517      * Tests that when an {@link Activity} is running on one display but is started from a second
518      * display then the {@link Activity} is moved to the second display.
519      */
520     @Test
testLaunchExistingActivityReparentDisplay()521     public void testLaunchExistingActivityReparentDisplay() {
522         // Create new virtual display.
523         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
524                 .setSimulateDisplay(true).createDisplay();
525 
526         launchActivityOnDisplay(SECOND_ACTIVITY, DEFAULT_DISPLAY);
527 
528         waitAndAssertTopResumedActivity(SECOND_ACTIVITY, DEFAULT_DISPLAY,
529                 "Must launch activity on same display.");
530 
531         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId,
532                 extraBool(KEY_USE_APPLICATION_CONTEXT, true), extraBool(KEY_NEW_TASK, true),
533                 extraBool(KEY_LAUNCH_ACTIVITY, true), extraBool(KEY_LAUNCH_IMPLICIT, true),
534                 extraString(KEY_ACTION, IMPLICIT_TARGET_SECOND_TEST_ACTION));
535 
536         waitAndAssertTopResumedActivity(IMPLICIT_TARGET_SECOND_ACTIVITY, newDisplay.mId,
537                 "Must launch activity on same display.");
538     }
539 
540     /**
541      * Tests launching an activity to secondary display from activity on primary display.
542      */
543     @Test
testLaunchActivityFromAppToSecondaryDisplay()544     public void testLaunchActivityFromAppToSecondaryDisplay() {
545         // Start launching activity.
546         launchActivity(LAUNCHING_ACTIVITY);
547 
548         // Create new simulated display.
549         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
550                 .setSimulateDisplay(true)
551                 .createDisplay();
552 
553         // Launch activity on secondary display from the app on primary display.
554         getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
555                 .setDisplayId(newDisplay.mId).execute();
556 
557         // Check that activity is launched on external display.
558         waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
559                 "Activity launched on secondary display must be focused");
560         assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, LAUNCHING_ACTIVITY),
561                 pair(newDisplay.mId, TEST_ACTIVITY));
562     }
563 
564     /** Tests that launching app from pending activity queue on external display is allowed. */
565     @Test
testLaunchPendingActivityOnSecondaryDisplay()566     public void testLaunchPendingActivityOnSecondaryDisplay() {
567         pressHomeButton();
568         // Create new simulated display.
569         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
570                 .setSimulateDisplay(true)
571                 .createDisplay();
572         final Bundle bundle = ActivityOptions.makeBasic().
573                 setLaunchDisplayId(newDisplay.mId).toBundle();
574         final Intent intent = new Intent(Intent.ACTION_VIEW)
575                 .setComponent(SECOND_ACTIVITY)
576                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
577                 .putExtra(KEY_LAUNCH_ACTIVITY, true)
578                 .putExtra(KEY_NEW_TASK, true);
579         mContext.startActivity(intent, bundle);
580 
581         // If home key was pressed, stopAppSwitches will be called.
582         // Since this test case is not start activity from shell, it won't grant
583         // STOP_APP_SWITCHES and this activity should be put into pending activity queue
584         // and this activity should been launched after
585         // ActivityTaskManagerService.APP_SWITCH_DELAY_TIME
586         mWmState.waitForPendingActivityContain(SECOND_ACTIVITY);
587         // If the activity is not pending, skip this test.
588         mWmState.assumePendingActivityContain(SECOND_ACTIVITY);
589         // In order to speed up test case without waiting for APP_SWITCH_DELAY_TIME, we launch
590         // another activity with LaunchActivityBuilder, in this way the activity can be start
591         // directly and also trigger pending activity to be launched.
592         getLaunchActivityBuilder()
593                 .setTargetActivity(THIRD_ACTIVITY)
594                 .execute();
595         mWmState.waitForValidState(SECOND_ACTIVITY);
596         waitAndAssertTopResumedActivity(THIRD_ACTIVITY, DEFAULT_DISPLAY,
597                 "Top activity must be the newly launched one");
598         mWmState.assertVisibility(SECOND_ACTIVITY, true);
599         assertEquals("Activity launched by app on secondary display must be on that display",
600                 newDisplay.mId, mWmState.getDisplayByActivity(SECOND_ACTIVITY));
601     }
602 
603     /**
604      * Tests that when an activity is launched with displayId specified and there is an existing
605      * matching task on some other display - that task will moved to the target display.
606      */
607     @Test
testMoveToDisplayOnLaunch()608     public void testMoveToDisplayOnLaunch() {
609         // Launch activity with unique affinity, so it will the only one in its task.
610         launchActivity(LAUNCHING_ACTIVITY);
611 
612         // Create new virtual display.
613         final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
614         mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
615         // Launch something to that display so that a new task is created. We need this to be
616         // able to compare task numbers in tasks later.
617         launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
618         mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
619 
620         final int rootTaskNum = mWmState.getDisplay(DEFAULT_DISPLAY).getRootTasks().size();
621         final int rootTaskNumOnSecondary =
622                 mWmState.getDisplay(newDisplay.mId).getRootTasks().size();
623 
624         // Launch activity on new secondary display.
625         // Using custom command here, because normally we add flags
626         // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}
627         // when launching on some specific display. We don't do it here as we want an existing
628         // task to be used.
629         final String launchCommand = "am start -n " + getActivityName(LAUNCHING_ACTIVITY)
630                 + " --display " + newDisplay.mId;
631         executeShellCommand(launchCommand);
632 
633         // Check that activity is brought to front.
634         waitAndAssertActivityStateOnDisplay(LAUNCHING_ACTIVITY, STATE_RESUMED, newDisplay.mId,
635                 "Existing task must be brought to front");
636 
637         // Check that task has moved from primary display to secondary.
638         // Since it is 1-to-1 relationship between task and root task for standard type &
639         // fullscreen activity, we check the number of root tasks here
640         final int rootTaskNumFinal = mWmState.getDisplay(DEFAULT_DISPLAY).getRootTasks().size();
641         assertEquals(
642                 "Root task number in default root task must be decremented.",
643                 rootTaskNum - 1,
644                 rootTaskNumFinal);
645         final int rootTaskNumFinalOnSecondary =
646                 mWmState.getDisplay(newDisplay.mId).getRootTasks().size();
647         assertEquals("Root task number on external display must be incremented.",
648                 rootTaskNumOnSecondary + 1, rootTaskNumFinalOnSecondary);
649     }
650 
651     /**
652      * Tests that when an activity is launched with displayId specified and there is an existing
653      * matching task on some other display - that task will moved to the target display.
654      */
655     @Test
testMoveToEmptyDisplayOnLaunch()656     public void testMoveToEmptyDisplayOnLaunch() {
657         // Launch activity with unique affinity, so it will the only one in its task. And choose
658         // resizeable activity to prevent the test activity be relaunched when launch it to another
659         // display, which may affect on this test case.
660         launchActivity(RESIZEABLE_ACTIVITY);
661 
662         // Create new virtual display.
663         final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
664         mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
665 
666         final int rootTaskNum = mWmState.getDisplay(DEFAULT_DISPLAY).getRootTasks().size();
667 
668         // Launch activity on new secondary display.
669         // Using custom command here, because normally we add flags
670         // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}
671         // when launching on some specific display. We don't do it here as we want an existing
672         // task to be used.
673         final String launchCommand = "am start -n " + getActivityName(RESIZEABLE_ACTIVITY)
674                 + " --display " + newDisplay.mId;
675         executeShellCommand(launchCommand);
676 
677         // Check that activity is brought to front.
678         waitAndAssertActivityStateOnDisplay(RESIZEABLE_ACTIVITY, STATE_RESUMED, newDisplay.mId,
679                 "Existing task must be brought to front");
680 
681         // Check that task has moved from primary display to secondary.
682         final int rootTaskNumFinal = mWmState.getDisplay(DEFAULT_DISPLAY).getRootTasks().size();
683         assertEquals(
684                 "Root task number in default root task must be decremented.",
685                 rootTaskNum - 1,
686                 rootTaskNumFinal);
687     }
688 
689     /**
690      * Tests that if a second task has the same affinity as a running task but in a separate
691      * process the second task launches in the same display.
692      */
693     @Test
testLaunchSameAffinityLaunchesSameDisplay()694     public void testLaunchSameAffinityLaunchesSameDisplay() {
695         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
696                 .setSimulateDisplay(true).createDisplay();
697 
698         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
699         mWmState.computeState(LAUNCHING_ACTIVITY);
700 
701         // Check that activity is on the secondary display.
702         final int frontRootTaskId = mWmState.getFrontRootTaskId(newDisplay.mId);
703         final Task firstFrontRootTask = mWmState.getRootTask(frontRootTaskId);
704         assertEquals("Activity launched on secondary display must be resumed",
705                 getActivityName(LAUNCHING_ACTIVITY),
706                 firstFrontRootTask.getResumedActivity());
707         mWmState.assertFocusedRootTask("Top root task must be on secondary display",
708                 frontRootTaskId);
709 
710         executeShellCommand("am start -n " + getActivityName(ALT_LAUNCHING_ACTIVITY));
711         mWmState.waitForValidState(ALT_LAUNCHING_ACTIVITY);
712 
713         // Check that second activity gets launched on the default display despite
714         // the affinity match on the secondary display.
715         final int displayFrontRootTaskId = mWmState.getFrontRootTaskId(newDisplay.mId);
716         final Task displayFrontRootTask = mWmState.getRootTask(displayFrontRootTaskId);
717         waitAndAssertTopResumedActivity(ALT_LAUNCHING_ACTIVITY, newDisplay.mId,
718                 "Activity launched on same display must be resumed");
719         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
720         waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
721                 "Existing task must be brought to front");
722 
723         // Check that the third intent is redirected to the first task due to the root
724         // component match on the secondary display.
725         final Task secondFrontRootTask = mWmState.getRootTask(frontRootTaskId);
726         final int secondFrontRootTaskId = mWmState.getFrontRootTaskId(newDisplay.mId);
727         assertEquals("Activity launched on secondary display must be resumed",
728                 getActivityName(ALT_LAUNCHING_ACTIVITY),
729                 displayFrontRootTask.getResumedActivity());
730         mWmState.assertFocusedRootTask(
731                 "Top root task must be on primary display", secondFrontRootTaskId);
732         assertEquals("Second display must contain 2 root tasks", 2,
733                 mWmState.getDisplay(newDisplay.mId).getRootTasks().size());
734         assertEquals("Top task must contain 2 activities", 2,
735                 secondFrontRootTask.getActivities().size());
736     }
737 
738     /**
739      * Tests that an activity is launched on the preferred display where the caller resided when
740      * both displays have matching tasks.
741      */
742     @Test
testTaskMatchOrderAcrossDisplays()743     public void testTaskMatchOrderAcrossDisplays() {
744         getLaunchActivityBuilder().setUseInstrumentation()
745                 .setTargetActivity(TEST_ACTIVITY).setNewTask(true)
746                 .setDisplayId(DEFAULT_DISPLAY).execute();
747         final int rootTaskId = mWmState.getFrontRootTaskId(DEFAULT_DISPLAY);
748         final int taskId = mWmState.getRootTask(rootTaskId).getTaskId();
749 
750         getLaunchActivityBuilder().setUseInstrumentation()
751                 .setTargetActivity(BROADCAST_RECEIVER_ACTIVITY).setNewTask(true)
752                 .setDisplayId(DEFAULT_DISPLAY).execute();
753 
754         mayLaunchHomeActivityForCar();
755 
756         final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
757         getLaunchActivityBuilder().setUseInstrumentation().setWithShellPermission(true)
758                 .setTargetActivity(TEST_ACTIVITY).setNewTask(true)
759                 .setDisplayId(newDisplay.mId).execute();
760         assertNotEquals("Top focus root task should not be on default display",
761                 taskId, mWmState.getRootTask(mWmState.getFocusedTaskId()).getTaskId());
762 
763         mBroadcastActionTrigger.launchActivityNewTask(getActivityName(TEST_ACTIVITY));
764         waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
765                 "Activity must be launched on default display");
766         mWmState.assertFocusedRootTask("Top focus root task must be on the default display",
767                 rootTaskId);
768     }
769 
770     /**
771      * Tests that the task affinity search respects the launch display id.
772      */
773     @Test
testLaunchDisplayAffinityMatch()774     public void testLaunchDisplayAffinityMatch() {
775         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
776                 .setSimulateDisplay(true).createDisplay();
777 
778         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
779 
780         // Check that activity is on the secondary display.
781         final int frontRootTaskId = mWmState.getFrontRootTaskId(newDisplay.mId);
782         final Task firstFrontRootTask = mWmState.getRootTask(frontRootTaskId);
783         assertEquals(
784                 "Activity launched on secondary display must be resumed",
785                 getActivityName(LAUNCHING_ACTIVITY),
786                 firstFrontRootTask.getResumedActivity());
787         mWmState.assertFocusedRootTask("Focus must be on secondary display", frontRootTaskId);
788 
789         // We don't want FLAG_ACTIVITY_MULTIPLE_TASK, so we can't use launchActivityOnDisplay
790         executeShellCommand("am start -n " + getActivityName(ALT_LAUNCHING_ACTIVITY)
791                 + " -f 0x10000000" // FLAG_ACTIVITY_NEW_TASK
792                 + " --display " + newDisplay.mId);
793         mWmState.computeState(ALT_LAUNCHING_ACTIVITY);
794 
795         // Check that second activity gets launched into the affinity matching
796         // task on the secondary display
797         final int secondFrontRootTaskId = mWmState.getFrontRootTaskId(newDisplay.mId);
798         final Task secondFrontRootTask =
799                 mWmState.getRootTask(secondFrontRootTaskId);
800         assertEquals("Activity launched on secondary display must be resumed",
801                 getActivityName(ALT_LAUNCHING_ACTIVITY),
802                 secondFrontRootTask.getResumedActivity());
803         mWmState.assertFocusedRootTask("Top root task must be on secondary display",
804                 secondFrontRootTaskId);
805         assertEquals("Second display must only contain 1 task",
806                 1, mWmState.getDisplay(newDisplay.mId).getRootTasks().size());
807         assertEquals("Top root task must contain 2 activities",
808                 2, secondFrontRootTask.getActivities().size());
809     }
810 
811     /**
812      * Tests that a new activity launched by an activity will end up on the same display
813      * even if the root task is not on the top for the display.
814      */
815     @Test
testNewTaskSameDisplay()816     public void testNewTaskSameDisplay() {
817         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
818                 .setSimulateDisplay(true)
819                 .createDisplay();
820 
821         launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
822 
823         // Check that the first activity is launched onto the secondary display
824         waitAndAssertTopResumedActivity(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId,
825                 "Activity launched on secondary display must be resumed");
826 
827         executeShellCommand("am start -n " + getActivityName(TEST_ACTIVITY));
828 
829         // Check that the second activity is launched on the same display
830         waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
831                 "Activity launched on default display must be resumed");
832 
833         mBroadcastActionTrigger.launchActivityNewTask(getActivityName(LAUNCHING_ACTIVITY));
834 
835         // Check that the third activity ends up in a new task in the same display where the
836         // first activity lands
837         waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
838                 "Activity must be launched on secondary display");
839         assertEquals(
840                 "Secondary display must contain 2 root tasks",
841                 2,
842                 mWmState.getDisplay(newDisplay.mId).getRootTasks().size());
843     }
844 
845     /**
846      * Tests that a new task launched by an activity will end up on the same display
847      * even if the focused task is not on that activity's display.
848      */
849     @Test
testNewTaskDefaultDisplay()850     public void testNewTaskDefaultDisplay() {
851         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
852                 .setSimulateDisplay(true)
853                 .createDisplay();
854 
855         launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
856 
857         // Check that the first activity is launched onto the secondary display
858         waitAndAssertTopResumedActivity(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId,
859                 "Activity launched on secondary display must be resumed");
860 
861         launchActivityOnDisplay(SECOND_ACTIVITY, DEFAULT_DISPLAY);
862 
863         // Check that the second activity is launched on the default display because the affinity
864         // is different
865         waitAndAssertTopResumedActivity(SECOND_ACTIVITY, DEFAULT_DISPLAY,
866                 "Activity launched on default display must be resumed");
867         assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, SECOND_ACTIVITY),
868                 pair(newDisplay.mId, BROADCAST_RECEIVER_ACTIVITY));
869 
870         mBroadcastActionTrigger.launchActivityNewTask(getActivityName(LAUNCHING_ACTIVITY));
871 
872         // Check that the third activity ends up in a new task in the same display where the
873         // first activity lands
874         waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
875                 "Activity must be launched on secondary display");
876         assertEquals(
877                 "Secondary display must contain 2 root tasks",
878                 2,
879                 mWmState.getDisplay(newDisplay.mId).getRootTasks().size());
880         assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, SECOND_ACTIVITY),
881                 pair(newDisplay.mId, LAUNCHING_ACTIVITY));
882     }
883 
884     /**
885      * Test that launching an activity implicitly will end up on the same display
886      */
887     @Test
testLaunchingFromApplicationContext()888     public void testLaunchingFromApplicationContext() {
889         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
890                 .setSimulateDisplay(true)
891                 .createDisplay();
892 
893         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId,
894                 extraBool(KEY_LAUNCH_ACTIVITY, true), extraBool(KEY_LAUNCH_IMPLICIT, true),
895                 extraBool(KEY_NEW_TASK, true), extraBool(KEY_USE_APPLICATION_CONTEXT, true),
896                 extraString(KEY_ACTION, IMPLICIT_TARGET_SECOND_TEST_ACTION));
897         waitAndAssertTopResumedActivity(IMPLICIT_TARGET_SECOND_ACTIVITY, newDisplay.mId,
898                 "Implicitly launched activity must launch on the same display");
899     }
900 
901     /**
902      * Test that launching an activity from pending intent will end up on the same display
903      */
904     @Test
testLaunchingFromPendingIntent()905     public void testLaunchingFromPendingIntent() {
906         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
907                 .setSimulateDisplay(true)
908                 .createDisplay();
909 
910         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId,
911                 extraBool(KEY_LAUNCH_ACTIVITY, true),
912                 extraBool(KEY_LAUNCH_IMPLICIT, true),
913                 extraBool(KEY_NEW_TASK, true),
914                 extraBool(KEY_USE_APPLICATION_CONTEXT, true),
915                 extraBool(KEY_LAUNCH_PENDING, true),
916                 extraString(KEY_ACTION, IMPLICIT_TARGET_SECOND_TEST_ACTION));
917 
918         waitAndAssertTopResumedActivity(IMPLICIT_TARGET_SECOND_ACTIVITY, newDisplay.mId,
919                 "Activity launched from pending intent must launch on the same display");
920     }
921 
922     /**
923      * Tests than an immediate launch after new display creation is handled correctly.
924      */
925     @Test
testImmediateLaunchOnNewDisplay()926     public void testImmediateLaunchOnNewDisplay() {
927         // Create new virtual display and immediately launch an activity on it.
928         SurfaceView surfaceView = new SurfaceView(mContext);
929         final VirtualDisplay virtualDisplay = mDm.createVirtualDisplay(
930                 "testImmediateLaunchOnNewDisplay", /*width=*/ 400, /*height=*/ 400,
931                 /*densityDpi=*/ 320, surfaceView.getHolder().getSurface(),
932                 DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
933         try {
934             int displayId = virtualDisplay.getDisplay().getDisplayId();
935             ComponentName componentName = new ComponentName(mContext,
936                     ImmediateLaunchTestActivity.class);
937             getLaunchActivityBuilder().setTargetActivity(componentName).setDisplayId(
938                     displayId).setUseInstrumentation().execute();
939 
940             // Check that activity is launched and placed correctly.
941             waitAndAssertActivityStateOnDisplay(componentName, STATE_RESUMED, displayId,
942                     "Test activity must be on top");
943             final int frontRootTaskId = mWmState.getFrontRootTaskId(displayId);
944             final Task firstFrontRootTask = mWmState.getRootTask(frontRootTaskId);
945             assertEquals("Activity launched on secondary display must be resumed",
946                     getActivityName(componentName), firstFrontRootTask.getResumedActivity());
947         } finally {
948             virtualDisplay.release();
949         }
950 
951     }
952 
953     @Test
testLaunchPendingIntentActivity()954     public void testLaunchPendingIntentActivity() throws Exception {
955         final DisplayContent displayContent = createManagedVirtualDisplaySession()
956                 .setSimulateDisplay(true)
957                 .createDisplay();
958 
959         // Activity should be launched on primary display by default.
960         getPendingIntentActivity(TEST_ACTIVITY).send();
961         waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
962                 "Activity launched on primary display and on top");
963 
964         final int resultCode = 1;
965         // Activity should be launched on target display according to the caller context.
966         final Context displayContext =
967                 mContext.createDisplayContext(mDm.getDisplay(displayContent.mId));
968         getPendingIntentActivity(TOP_ACTIVITY).send(displayContext, resultCode, null /* intent */);
969         waitAndAssertTopResumedActivity(TOP_ACTIVITY, displayContent.mId,
970                 "Activity launched on secondary display and on top");
971 
972         // Activity should be brought to front on the same display if it already existed.
973         getPendingIntentActivity(TEST_ACTIVITY).send(displayContext, resultCode, null /* intent */);
974         waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
975                 "Activity launched on primary display and on top");
976 
977         mayLaunchHomeActivityForCar();
978         // Activity should be moved to target display.
979         final ActivityOptions options = ActivityOptions.makeBasic();
980         options.setLaunchDisplayId(displayContent.mId);
981         getPendingIntentActivity(TEST_ACTIVITY).send(mContext, resultCode, null /* intent */,
982                 null /* onFinished */, null /* handler */, null /* requiredPermission */,
983                 options.toBundle());
984         waitAndAssertTopResumedActivity(TEST_ACTIVITY, displayContent.mId,
985                 "Activity launched on secondary display and on top");
986     }
987 
988     @Test
testLaunchActivityClearTask()989     public void testLaunchActivityClearTask() {
990         assertBroughtExistingTaskToAnotherDisplay(FLAG_ACTIVITY_CLEAR_TASK, LAUNCHING_ACTIVITY);
991     }
992 
993     @Test
testLaunchActivityClearTop()994     public void testLaunchActivityClearTop() {
995         assertBroughtExistingTaskToAnotherDisplay(FLAG_ACTIVITY_CLEAR_TOP, LAUNCHING_ACTIVITY);
996     }
997 
998     @Test
testLaunchActivitySingleTop()999     public void testLaunchActivitySingleTop() {
1000         assertBroughtExistingTaskToAnotherDisplay(FLAG_ACTIVITY_SINGLE_TOP, TEST_ACTIVITY);
1001     }
1002 
1003     @Test
testLaunchActivitySingleTopOnNewDisplay()1004     public void testLaunchActivitySingleTopOnNewDisplay() {
1005         launchActivity(SINGLE_TOP_ACTIVITY);
1006         waitAndAssertTopResumedActivity(SINGLE_TOP_ACTIVITY, DEFAULT_DISPLAY,
1007                 "Activity launched on primary display and on top");
1008         final int taskId = mWmState.getTaskByActivity(SINGLE_TOP_ACTIVITY).getTaskId();
1009 
1010         // Create new virtual display.
1011         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
1012                 .setSimulateDisplay(true)
1013                 .createDisplay();
1014 
1015         mayLaunchHomeActivityForCar();
1016         // Launch activity on new secondary display.
1017         getLaunchActivityBuilder()
1018                 .setUseInstrumentation()
1019                 .setTargetActivity(SINGLE_TOP_ACTIVITY)
1020                 .allowMultipleInstances(false)
1021                 .setDisplayId(newDisplay.mId).execute();
1022 
1023         waitAndAssertTopResumedActivity(SINGLE_TOP_ACTIVITY, newDisplay.mId,
1024                 "Activity launched on secondary display must be on top");
1025 
1026         final int taskId2 = mWmState.getTaskByActivity(SINGLE_TOP_ACTIVITY).getTaskId();
1027         assertEquals("Activity must be in the same task.", taskId, taskId2);
1028         assertEquals("Activity is the only member of its task", 1,
1029                 mWmState.getActivityCountInTask(taskId2, null));
1030     }
1031 
1032     /**
1033      * This test case tests the behavior that a fullscreen activity was started on top of the
1034      * no-history activity from default display while sleeping. The no-history activity from
1035      * the external display should not be finished.
1036      */
1037     @Test
testLaunchNoHistoryActivityOnNewDisplay()1038     public void testLaunchNoHistoryActivityOnNewDisplay() {
1039         launchActivity(NO_HISTORY_ACTIVITY);
1040         waitAndAssertTopResumedActivity(NO_HISTORY_ACTIVITY, DEFAULT_DISPLAY,
1041                 "Activity launched on primary display and on top");
1042 
1043         final int taskId = mWmState.getTaskByActivity(NO_HISTORY_ACTIVITY).getTaskId();
1044 
1045         final PrimaryDisplayStateSession displayStateSession =
1046                 mObjectTracker.manage(new PrimaryDisplayStateSession());
1047 
1048         // Create new virtual display.
1049         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
1050                 .setSimulateDisplay(true)
1051                 .createDisplay();
1052 
1053         launchActivityOnDisplay(NO_HISTORY_ACTIVITY2, newDisplay.mId);
1054 
1055         // Check that the activity is resumed on the external display
1056         waitAndAssertActivityStateOnDisplay(NO_HISTORY_ACTIVITY2, STATE_RESUMED, newDisplay.mId,
1057                 "Activity launched on external display must be resumed");
1058         final int taskId2 = mWmState.getTaskByActivity(NO_HISTORY_ACTIVITY2).getTaskId();
1059 
1060         displayStateSession.turnScreenOff();
1061         launchActivityOnDisplay(SHOW_WHEN_LOCKED_ACTIVITY, DEFAULT_DISPLAY);
1062 
1063         assertNotEquals("Activity must not be in the same task.", taskId, taskId2);
1064         assertEquals("No-history activity is the member of its task", 1,
1065                 mWmState.getActivityCountInTask(taskId2, NO_HISTORY_ACTIVITY2));
1066         assertFalse("No-history activity should not be finished.",
1067                 mWmState.hasActivityState(NO_HISTORY_ACTIVITY2, STATE_DESTROYED));
1068     }
1069 
assertBroughtExistingTaskToAnotherDisplay(int flags, ComponentName topActivity)1070     private void assertBroughtExistingTaskToAnotherDisplay(int flags, ComponentName topActivity) {
1071         // Start TEST_ACTIVITY on top of LAUNCHING_ACTIVITY within the same task
1072         getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute();
1073 
1074         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
1075                 .setSimulateDisplay(true)
1076                 .createDisplay();
1077 
1078         mayLaunchHomeActivityForCar();
1079         // Start LAUNCHING_ACTIVITY on secondary display with target flags, verify the task
1080         // be reparented to secondary display
1081         getLaunchActivityBuilder()
1082                 .setUseInstrumentation()
1083                 .setTargetActivity(LAUNCHING_ACTIVITY)
1084                 .setIntentFlags(flags)
1085                 .allowMultipleInstances(false)
1086                 .setDisplayId(newDisplay.mId).execute();
1087         waitAndAssertTopResumedActivity(topActivity, newDisplay.mId,
1088                 "Activity launched on secondary display and on top");
1089     }
1090 
mayLaunchHomeActivityForCar()1091     private void mayLaunchHomeActivityForCar() {
1092         if (isCar()) {
1093             // CarLauncher has a TaskView, and which launches the embedded task when it becomes
1094             // visible and this can disrupt the top focused state of the test activity.
1095             // So we'd like to make Home visible in advance to prevent that.
1096             launchHomeActivity();
1097         }
1098     }
1099 
getPendingIntentActivity(ComponentName activity)1100     private PendingIntent getPendingIntentActivity(ComponentName activity) {
1101         final Intent intent = new Intent();
1102         intent.setClassName(activity.getPackageName(), activity.getClassName());
1103         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1104         ActivityOptions options = ActivityOptions.makeBasic()
1105                 .setPendingIntentCreatorBackgroundActivityStartMode(
1106                     ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
1107         return PendingIntent.getActivity(mContext, 1 /* requestCode */, intent,
1108                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
1109                 options.toBundle());
1110     }
1111 
1112     public static class ImmediateLaunchTestActivity extends Activity {}
1113 }
1114