1/*
2 * Copyright (C) 2016 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
17package android.server.am;
18
19import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
20import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
21import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
22import static android.server.am.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
23import static android.server.am.ActivityLauncher.KEY_NEW_TASK;
24import static android.server.am.ActivityLauncher.KEY_TARGET_COMPONENT;
25import static android.server.am.ActivityManagerDisplayTestBase.ReportedDisplayMetrics.getDisplayMetrics;
26import static android.server.am.ActivityManagerState.STATE_RESUMED;
27import static android.server.am.ActivityManagerState.STATE_STOPPED;
28import static android.server.am.ComponentNameUtils.getActivityName;
29import static android.server.am.ComponentNameUtils.getWindowName;
30import static android.server.am.Components.ALT_LAUNCHING_ACTIVITY;
31import static android.server.am.Components.BROADCAST_RECEIVER_ACTIVITY;
32import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
33import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
34import static android.server.am.Components.LAUNCHING_ACTIVITY;
35import static android.server.am.Components.LAUNCH_BROADCAST_ACTION;
36import static android.server.am.Components.LAUNCH_BROADCAST_RECEIVER;
37import static android.server.am.Components.NON_RESIZEABLE_ACTIVITY;
38import static android.server.am.Components.RESIZEABLE_ACTIVITY;
39import static android.server.am.Components.SHOW_WHEN_LOCKED_ATTR_ACTIVITY;
40import static android.server.am.Components.TEST_ACTIVITY;
41import static android.server.am.Components.VIRTUAL_DISPLAY_ACTIVITY;
42import static android.server.am.StateLogger.logAlways;
43import static android.server.am.StateLogger.logE;
44import static android.server.am.UiDeviceUtils.pressSleepButton;
45import static android.server.am.UiDeviceUtils.pressWakeupButton;
46import static android.server.am.second.Components.SECOND_ACTIVITY;
47import static android.server.am.second.Components.SECOND_LAUNCH_BROADCAST_ACTION;
48import static android.server.am.second.Components.SECOND_LAUNCH_BROADCAST_RECEIVER;
49import static android.server.am.second.Components.SECOND_NO_EMBEDDING_ACTIVITY;
50import static android.server.am.third.Components.THIRD_ACTIVITY;
51import static android.view.Display.DEFAULT_DISPLAY;
52
53import static org.junit.Assert.assertEquals;
54import static org.junit.Assert.assertFalse;
55import static org.junit.Assert.assertNotNull;
56import static org.junit.Assert.assertNull;
57import static org.junit.Assert.assertTrue;
58import static org.junit.Assert.fail;
59import static org.junit.Assume.assumeTrue;
60
61import android.content.ComponentName;
62import android.os.SystemClock;
63import android.platform.test.annotations.Presubmit;
64import android.server.am.ActivityManagerState.ActivityDisplay;
65import android.support.test.filters.FlakyTest;
66
67import androidx.annotation.Nullable;
68
69import org.junit.Before;
70import org.junit.Test;
71
72import java.util.concurrent.TimeUnit;
73import java.util.List;
74import java.util.regex.Matcher;
75import java.util.regex.Pattern;
76
77/**
78 * Build/Install/Run:
79 *     atest CtsActivityManagerDeviceTestCases:ActivityManagerMultiDisplayTests
80 */
81@Presubmit
82@FlakyTest(bugId = 77652261)
83public class ActivityManagerMultiDisplayTests extends ActivityManagerDisplayTestBase {
84
85    // TODO(b/70247058): Use {@link Context#sendBroadcast(Intent).
86    // Shell command to finish {@link #BROADCAST_RECEIVER_ACTIVITY}.
87    private static final String FINISH_ACTIVITY_BROADCAST = "am broadcast -a "
88            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_FINISH_BROADCAST + " true";
89    // Shell command to launch activity via {@link #BROADCAST_RECEIVER_ACTIVITY}.
90    private static final String LAUNCH_ACTIVITY_BROADCAST = "am broadcast -a "
91            + ACTION_TRIGGER_BROADCAST + " --ez " + KEY_LAUNCH_ACTIVITY + " true --ez "
92            + KEY_NEW_TASK + " true --es " + KEY_TARGET_COMPONENT + " ";
93
94    @Before
95    @Override
96    public void setUp() throws Exception {
97        super.setUp();
98
99        assumeTrue(supportsMultiDisplay());
100    }
101
102    /**
103     * Tests launching an activity on virtual display.
104     */
105    @Test
106    public void testLaunchActivityOnSecondaryDisplay() throws Exception {
107        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
108            // Create new virtual display.
109            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
110
111            // Launch activity on new secondary display.
112            final LogSeparator logSeparator = separateLogs();
113            launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
114            mAmWmState.computeState(TEST_ACTIVITY);
115
116            mAmWmState.assertFocusedActivity(
117                    "Activity launched on secondary display must be focused",
118                    TEST_ACTIVITY);
119
120            // Check that activity is on the right display.
121            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
122            final ActivityManagerState.ActivityStack frontStack =
123                    mAmWmState.getAmState().getStackById(frontStackId);
124            assertEquals("Launched activity must be on the secondary display and resumed",
125                    getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity);
126            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
127
128            // Check that activity config corresponds to display config.
129            final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY,
130                    logSeparator);
131            assertEquals("Activity launched on secondary display must have proper configuration",
132                    CUSTOM_DENSITY_DPI, reportedSizes.densityDpi);
133        }
134    }
135
136    /**
137     * Tests launching an activity on primary display explicitly.
138     */
139    @Test
140    public void testLaunchActivityOnPrimaryDisplay() throws Exception {
141        // Launch activity on primary display explicitly.
142        launchActivityOnDisplay(LAUNCHING_ACTIVITY, 0);
143        mAmWmState.computeState(LAUNCHING_ACTIVITY);
144
145        mAmWmState.assertFocusedActivity("Activity launched on primary display must be focused",
146                LAUNCHING_ACTIVITY);
147
148        // Check that activity is on the right display.
149        int frontStackId = mAmWmState.getAmState().getFrontStackId(0 /* displayId */);
150        ActivityManagerState.ActivityStack frontStack
151                = mAmWmState.getAmState().getStackById(frontStackId);
152        assertEquals("Launched activity must be on the primary display and resumed",
153                getActivityName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity);
154        mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
155
156        // Launch another activity on primary display using the first one
157        getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).setNewTask(true)
158                .setMultipleTask(true).setDisplayId(0).execute();
159        mAmWmState.computeState(TEST_ACTIVITY);
160
161        mAmWmState.assertFocusedActivity("Activity launched on primary display must be focused",
162                TEST_ACTIVITY);
163
164        // Check that activity is on the right display.
165        frontStackId = mAmWmState.getAmState().getFrontStackId(0 /* displayId */);
166        frontStack = mAmWmState.getAmState().getStackById(frontStackId);
167        assertEquals("Launched activity must be on the primary display and resumed",
168                getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity);
169        mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
170    }
171
172    /**
173     * Tests launching a non-resizeable activity on virtual display. It should land on the
174     * default display.
175     */
176    @Test
177    public void testLaunchNonResizeableActivityOnSecondaryDisplay() throws Exception {
178        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
179            // Create new virtual display.
180            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
181
182            // Launch activity on new secondary display.
183            launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY, newDisplay.mId);
184            mAmWmState.computeState(NON_RESIZEABLE_ACTIVITY);
185
186            mAmWmState.assertFocusedActivity(
187                    "Activity launched on secondary display must be focused",
188                    NON_RESIZEABLE_ACTIVITY);
189
190            // Check that activity is on the right display.
191            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
192            final ActivityManagerState.ActivityStack frontStack =
193                    mAmWmState.getAmState().getStackById(frontStackId);
194            assertEquals("Launched activity must be on the primary display and resumed",
195                    getActivityName(NON_RESIZEABLE_ACTIVITY),
196                    frontStack.mResumedActivity);
197            mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
198        }
199    }
200
201    /**
202     * Tests launching a non-resizeable activity on virtual display while split-screen is active
203     * on the primary display. It should land on the primary display and dismiss docked stack.
204     */
205    @Test
206    public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception {
207        assumeTrue(supportsSplitScreenMultiWindow());
208
209        // Start launching activity.
210        launchActivitiesInSplitScreen(
211                getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
212                getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
213
214        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
215            // Create new virtual display.
216            final ActivityDisplay newDisplay = virtualDisplaySession.setLaunchInSplitScreen(true)
217                    .createDisplay();
218
219            // Launch activity on new secondary display.
220            launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY, newDisplay.mId);
221            mAmWmState.computeState(NON_RESIZEABLE_ACTIVITY);
222
223            mAmWmState.assertFocusedActivity(
224                    "Activity launched on secondary display must be focused",
225                    NON_RESIZEABLE_ACTIVITY);
226
227            // Check that activity is on the right display.
228            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
229            final ActivityManagerState.ActivityStack frontStack =
230                    mAmWmState.getAmState().getStackById(frontStackId);
231            assertEquals("Launched activity must be on the primary display and resumed",
232                    getActivityName(NON_RESIZEABLE_ACTIVITY),
233                    frontStack.mResumedActivity);
234            mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
235            mAmWmState.assertDoesNotContainStack("Must not contain docked stack.",
236                    WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
237        }
238    }
239
240    /**
241     * Tests moving a non-resizeable activity to a virtual display. It should stay on the default
242     * display with no action performed.
243     */
244    @Test
245    public void testMoveNonResizeableActivityToSecondaryDisplay() throws Exception {
246        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
247            // Create new virtual display.
248            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
249            // Launch a non-resizeable activity on a primary display.
250            launchActivityInNewTask(NON_RESIZEABLE_ACTIVITY);
251            // Launch a resizeable activity on new secondary display to create a new stack there.
252            launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
253            final int externalFrontStackId = mAmWmState.getAmState()
254                    .getFrontStackId(newDisplay.mId);
255
256            // Try to move the non-resizeable activity to new secondary display.
257            moveActivityToStack(NON_RESIZEABLE_ACTIVITY, externalFrontStackId);
258            mAmWmState.computeState(NON_RESIZEABLE_ACTIVITY);
259
260            mAmWmState.assertFocusedActivity(
261                    "Activity launched on secondary display must be focused",
262                    RESIZEABLE_ACTIVITY);
263
264            // Check that activity is in the same stack
265            final int defaultFrontStackId = mAmWmState.getAmState().getFrontStackId(
266                    DEFAULT_DISPLAY);
267            final ActivityManagerState.ActivityStack defaultFrontStack =
268                    mAmWmState.getAmState().getStackById(defaultFrontStackId);
269            assertEquals("Launched activity must be on the primary display and resumed",
270                    getActivityName(NON_RESIZEABLE_ACTIVITY),
271                    defaultFrontStack.getTopTask().mRealActivity);
272            mAmWmState.assertFocusedStack("Focus must remain on the secondary display",
273                    externalFrontStackId);
274        }
275    }
276
277    /**
278     * Tests launching a non-resizeable activity on virtual display from activity there. It should
279     * land on the secondary display based on the resizeability of the root activity of the task.
280     */
281    @Test
282    public void testLaunchNonResizeableActivityFromSecondaryDisplaySameTask() throws Exception {
283        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
284            // Create new simulated display.
285            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
286                    .createDisplay();
287
288            // Launch activity on new secondary display.
289            launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
290            mAmWmState.assertFocusedActivity(
291                    "Activity launched on secondary display must be focused",
292                    BROADCAST_RECEIVER_ACTIVITY);
293
294            // Check that launching activity is on the secondary display.
295            int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
296            ActivityManagerState.ActivityStack frontStack =
297                    mAmWmState.getAmState().getStackById(frontStackId);
298            assertEquals("Launched activity must be on the secondary display and resumed",
299                    getActivityName(BROADCAST_RECEIVER_ACTIVITY),
300                    frontStack.mResumedActivity);
301            mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
302
303            // Launch non-resizeable activity from secondary display.
304            executeShellCommand(
305                    LAUNCH_ACTIVITY_BROADCAST + getActivityName(NON_RESIZEABLE_ACTIVITY));
306            mAmWmState.computeState(NON_RESIZEABLE_ACTIVITY);
307
308            // Check that non-resizeable activity is on the secondary display, because of the
309            // resizeable root of the task.
310            frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
311            frontStack = mAmWmState.getAmState().getStackById(frontStackId);
312            assertEquals("Launched activity must be on the primary display and resumed",
313                    getActivityName(NON_RESIZEABLE_ACTIVITY),
314                    frontStack.mResumedActivity);
315            mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
316        }
317    }
318
319    /**
320     * Tests launching a non-resizeable activity on virtual display from activity there. It should
321     * land on some different suitable display (usually - on the default one).
322     */
323    @Test
324    public void testLaunchNonResizeableActivityFromSecondaryDisplayNewTask() throws Exception {
325        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
326            // Create new virtual display.
327            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
328
329            // Launch activity on new secondary display.
330            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
331            mAmWmState.assertFocusedActivity(
332                    "Activity launched on secondary display must be focused",
333                    LAUNCHING_ACTIVITY);
334
335            // Check that launching activity is on the secondary display.
336            int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
337            ActivityManagerState.ActivityStack frontStack =
338                    mAmWmState.getAmState().getStackById(frontStackId);
339            assertEquals("Launched activity must be on the secondary display and resumed",
340                    getActivityName(LAUNCHING_ACTIVITY),
341                    frontStack.mResumedActivity);
342            mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
343
344            // Launch non-resizeable activity from secondary display.
345            getLaunchActivityBuilder().setTargetActivity(NON_RESIZEABLE_ACTIVITY)
346                    .setNewTask(true).setMultipleTask(true).execute();
347
348            // Check that non-resizeable activity is on the primary display.
349            frontStackId = mAmWmState.getAmState().getFocusedStackId();
350            frontStack = mAmWmState.getAmState().getStackById(frontStackId);
351            assertFalse("Launched activity must be on a different display",
352                    newDisplay.mId == frontStack.mDisplayId);
353            assertEquals("Launched activity must be resumed",
354                    getActivityName(NON_RESIZEABLE_ACTIVITY),
355                    frontStack.mResumedActivity);
356            mAmWmState.assertFocusedStack("Focus must be on a just launched activity",
357                    frontStackId);
358        }
359    }
360
361    /**
362     * Tests launching an activity on a virtual display without special permission must not be
363     * allowed.
364     */
365    @Test
366    public void testLaunchWithoutPermissionOnVirtualDisplay() throws Exception {
367        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
368            // Create new virtual display.
369            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
370
371            final LogSeparator logSeparator = separateLogs();
372
373            // Try to launch an activity and check it security exception was triggered.
374            getLaunchActivityBuilder()
375                    .setUseBroadcastReceiver(SECOND_LAUNCH_BROADCAST_RECEIVER,
376                            SECOND_LAUNCH_BROADCAST_ACTION)
377                    .setDisplayId(newDisplay.mId)
378                    .setTargetActivity(TEST_ACTIVITY)
379                    .execute();
380
381            assertSecurityException("ActivityLauncher", logSeparator);
382
383            mAmWmState.computeState(TEST_ACTIVITY);
384            assertFalse("Restricted activity must not be launched",
385                    mAmWmState.getAmState().containsActivity(TEST_ACTIVITY));
386        }
387    }
388
389    /**
390     * Tests launching an activity on a virtual display without special permission must be allowed
391     * for activities with same UID.
392     */
393    @Test
394    public void testLaunchWithoutPermissionOnVirtualDisplayByOwner() throws Exception {
395        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
396            // Create new virtual display.
397            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
398
399            // Try to launch an activity and check it security exception was triggered.
400            getLaunchActivityBuilder()
401                    .setUseBroadcastReceiver(LAUNCH_BROADCAST_RECEIVER, LAUNCH_BROADCAST_ACTION)
402                    .setDisplayId(newDisplay.mId)
403                    .setTargetActivity(TEST_ACTIVITY)
404                    .execute();
405
406            mAmWmState.waitForValidState(TEST_ACTIVITY);
407
408            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
409            final ActivityManagerState.ActivityStack focusedStack =
410                    mAmWmState.getAmState().getStackById(externalFocusedStackId);
411            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
412                    focusedStack.mDisplayId);
413
414            mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
415                    TEST_ACTIVITY);
416            assertEquals("Activity launched by owner must be on external display",
417                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
418        }
419    }
420
421    /**
422     * Tests launching an activity on virtual display and then launching another activity via shell
423     * command and without specifying the display id - the second activity must appear on the
424     * primary display.
425     */
426    @Test
427    public void testConsequentLaunchActivity() throws Exception {
428        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
429            // Create new virtual display.
430            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
431
432            // Launch activity on new secondary display.
433            launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
434            mAmWmState.computeState(TEST_ACTIVITY);
435
436            mAmWmState.assertFocusedActivity(
437                    "Activity launched on secondary display must be focused",
438                    TEST_ACTIVITY);
439
440            // Launch second activity without specifying display.
441            launchActivity(LAUNCHING_ACTIVITY);
442            mAmWmState.computeState(LAUNCHING_ACTIVITY);
443
444            // Check that activity is launched in focused stack on primary display.
445            mAmWmState.assertFocusedActivity("Launched activity must be focused",
446                    LAUNCHING_ACTIVITY);
447            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
448            final ActivityManagerState.ActivityStack frontStack =
449                    mAmWmState.getAmState().getStackById(frontStackId);
450            assertEquals("Launched activity must be resumed in front stack",
451                    getActivityName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity);
452            assertEquals("Front stack must be on primary display",
453                    DEFAULT_DISPLAY, frontStack.mDisplayId);
454        }
455    }
456
457    /**
458     * Tests launching an activity on simulated display and then launching another activity from the
459     * first one - it must appear on the secondary display, because it was launched from there.
460     */
461    @Test
462    public void testConsequentLaunchActivityFromSecondaryDisplay() throws Exception {
463        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
464            // Create new simulated display.
465            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
466                    .createDisplay();
467
468            // Launch activity on new secondary display.
469            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
470            mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
471
472            mAmWmState.assertFocusedActivity(
473                    "Activity launched on secondary display must be resumed",
474                    LAUNCHING_ACTIVITY);
475
476            // Launch second activity from app on secondary display without specifying display id.
477            getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute();
478            mAmWmState.computeState(TEST_ACTIVITY);
479
480            // Check that activity is launched in focused stack on external display.
481            mAmWmState.assertFocusedActivity("Launched activity must be focused",
482                    TEST_ACTIVITY);
483            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
484            final ActivityManagerState.ActivityStack frontStack =
485                    mAmWmState.getAmState().getStackById(frontStackId);
486            assertEquals("Launched activity must be resumed in front stack",
487                    getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity);
488        }
489    }
490
491    /**
492     * Tests launching an activity on virtual display and then launching another activity from the
493     * first one - it must appear on the secondary display, because it was launched from there.
494     */
495    @Test
496    public void testConsequentLaunchActivityFromVirtualDisplay() throws Exception {
497        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
498            // Create new virtual display.
499            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
500
501            // Launch activity on new secondary display.
502            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
503            mAmWmState.computeState(LAUNCHING_ACTIVITY);
504
505            mAmWmState.assertFocusedActivity(
506                    "Activity launched on secondary display must be resumed",
507                    LAUNCHING_ACTIVITY);
508
509            // Launch second activity from app on secondary display without specifying display id.
510            getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute();
511            mAmWmState.computeState(TEST_ACTIVITY);
512
513            // Check that activity is launched in focused stack on external display.
514            mAmWmState.assertFocusedActivity("Launched activity must be focused",
515                    TEST_ACTIVITY);
516            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
517            final ActivityManagerState.ActivityStack frontStack = mAmWmState.getAmState()
518                    .getStackById(frontStackId);
519            assertEquals("Launched activity must be resumed in front stack",
520                    getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity);
521        }
522    }
523
524    /**
525     * Tests launching an activity on virtual display and then launching another activity from the
526     * first one with specifying the target display - it must appear on the secondary display.
527     */
528    @Test
529    public void testConsequentLaunchActivityFromVirtualDisplayToTargetDisplay() throws Exception {
530        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
531            // Create new virtual display.
532            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
533
534            // Launch activity on new secondary display.
535            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
536            mAmWmState.computeState(LAUNCHING_ACTIVITY);
537
538            mAmWmState.assertFocusedActivity(
539                    "Activity launched on secondary display must be resumed",
540                    LAUNCHING_ACTIVITY);
541
542            // Launch second activity from app on secondary display specifying same display id.
543            getLaunchActivityBuilder()
544                    .setTargetActivity(SECOND_ACTIVITY)
545                    .setDisplayId(newDisplay.mId)
546                    .execute();
547            mAmWmState.computeState(TEST_ACTIVITY);
548
549            // Check that activity is launched in focused stack on external display.
550            mAmWmState.assertFocusedActivity("Launched activity must be focused", SECOND_ACTIVITY);
551            int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
552            ActivityManagerState.ActivityStack frontStack =
553                    mAmWmState.getAmState().getStackById(frontStackId);
554            assertEquals("Launched activity must be resumed in front stack",
555                    getActivityName(SECOND_ACTIVITY), frontStack.mResumedActivity);
556
557            // Launch other activity with different uid and check if it has launched successfully.
558            getLaunchActivityBuilder()
559                    .setUseBroadcastReceiver(SECOND_LAUNCH_BROADCAST_RECEIVER,
560                            SECOND_LAUNCH_BROADCAST_ACTION)
561                    .setDisplayId(newDisplay.mId)
562                    .setTargetActivity(THIRD_ACTIVITY)
563                    .execute();
564            mAmWmState.waitForValidState(THIRD_ACTIVITY);
565
566            // Check that activity is launched in focused stack on external display.
567            mAmWmState.assertFocusedActivity("Launched activity must be focused", THIRD_ACTIVITY);
568            frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
569            frontStack = mAmWmState.getAmState().getStackById(frontStackId);
570            assertEquals("Launched activity must be resumed in front stack",
571                    getActivityName(THIRD_ACTIVITY), frontStack.mResumedActivity);
572        }
573    }
574
575    /**
576     * Tests launching an activity on virtual display and then launching another activity that
577     * doesn't allow embedding - it should fail with security exception.
578     */
579    @Test
580    public void testConsequentLaunchActivityFromVirtualDisplayNoEmbedding() throws Exception {
581        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
582            // Create new virtual display.
583            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
584
585            // Launch activity on new secondary display.
586            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
587            mAmWmState.computeState(LAUNCHING_ACTIVITY);
588
589            mAmWmState.assertFocusedActivity(
590                    "Activity launched on secondary display must be resumed",
591                    LAUNCHING_ACTIVITY);
592
593            final LogSeparator logSeparator = separateLogs();
594
595            // Launch second activity from app on secondary display specifying same display id.
596            getLaunchActivityBuilder()
597                    .setTargetActivity(SECOND_NO_EMBEDDING_ACTIVITY)
598                    .setDisplayId(newDisplay.mId)
599                    .execute();
600
601            assertSecurityException("ActivityLauncher", logSeparator);
602        }
603    }
604
605    /**
606     * Tests launching an activity to secondary display from activity on primary display.
607     */
608    @Test
609    public void testLaunchActivityFromAppToSecondaryDisplay() throws Exception {
610        // Start launching activity.
611        launchActivity(LAUNCHING_ACTIVITY);
612
613        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
614            // Create new simulated display.
615            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
616                    .createDisplay();
617
618            // Launch activity on secondary display from the app on primary display.
619            getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
620                    .setDisplayId(newDisplay.mId).execute();
621
622            // Check that activity is launched on external display.
623            mAmWmState.computeState(TEST_ACTIVITY);
624            mAmWmState.assertFocusedActivity(
625                    "Activity launched on secondary display must be focused",
626                    TEST_ACTIVITY);
627            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
628            final ActivityManagerState.ActivityStack frontStack =
629                    mAmWmState.getAmState().getStackById(frontStackId);
630            assertEquals("Launched activity must be resumed in front stack",
631                    getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity);
632        }
633    }
634
635    /**
636     * Tests launching activities on secondary and then on primary display to see if the stack
637     * visibility is not affected.
638     */
639    @Test
640    public void testLaunchActivitiesAffectsVisibility() throws Exception {
641        // Start launching activity.
642        launchActivity(LAUNCHING_ACTIVITY);
643
644        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
645            // Create new virtual display.
646            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
647            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
648
649            // Launch activity on new secondary display.
650            launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
651            mAmWmState.assertVisibility(TEST_ACTIVITY, true /* visible */);
652            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
653
654            // Launch activity on primary display and check if it doesn't affect activity on
655            // secondary display.
656            getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY).execute();
657            mAmWmState.waitForValidState(RESIZEABLE_ACTIVITY);
658            mAmWmState.assertVisibility(TEST_ACTIVITY, true /* visible */);
659            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
660        }
661    }
662
663    /**
664     * Test that move-task works when moving between displays.
665     */
666    @Test
667    public void testMoveTaskBetweenDisplays() throws Exception {
668        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
669            // Create new virtual display.
670            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
671            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
672            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
673                    VIRTUAL_DISPLAY_ACTIVITY);
674            final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
675            ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
676                    defaultDisplayStackId);
677            assertEquals("Focus must remain on primary display",
678                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
679
680            // Launch activity on new secondary display.
681            launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
682            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
683                    TEST_ACTIVITY);
684            int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
685            focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
686            assertEquals("Focused stack must be on secondary display",
687                    newDisplay.mId, focusedStack.mDisplayId);
688
689            // Move activity from secondary display to primary.
690            moveActivityToStack(TEST_ACTIVITY, defaultDisplayStackId);
691            mAmWmState.waitForFocusedStack(defaultDisplayStackId);
692            mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY);
693            focusedStackId = mAmWmState.getAmState().getFocusedStackId();
694            focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
695            assertEquals("Focus must return to primary display",
696                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
697        }
698    }
699
700    /**
701     * Tests launching activities on secondary display and then removing it to see if stack focus
702     * is moved correctly.
703     * This version launches virtual display creator to fullscreen stack in split-screen.
704     */
705    @Test
706    public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
707        assumeTrue(supportsSplitScreenMultiWindow());
708
709        // Start launching activity into docked stack.
710        launchActivitiesInSplitScreen(
711                getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
712                getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
713        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
714
715        tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
716                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
717    }
718
719    /**
720     * Tests launching activities on secondary display and then removing it to see if stack focus
721     * is moved correctly.
722     * This version launches virtual display creator to docked stack in split-screen.
723     */
724    @Test
725    public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
726        assumeTrue(supportsSplitScreenMultiWindow());
727
728        // Setup split-screen.
729        launchActivitiesInSplitScreen(
730                getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY),
731                getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY));
732        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
733
734        tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
735                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
736    }
737
738    /**
739     * Tests launching activities on secondary display and then removing it to see if stack focus
740     * is moved correctly.
741     * This version works without split-screen.
742     */
743    @Test
744    public void testStackFocusSwitchOnDisplayRemoved3() throws Exception {
745        // Start an activity on default display to determine default stack.
746        launchActivity(BROADCAST_RECEIVER_ACTIVITY);
747        final int focusedStackWindowingMode = mAmWmState.getAmState().getFrontStackWindowingMode(
748                DEFAULT_DISPLAY);
749        // Finish probing activity.
750        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
751
752        tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */,
753                focusedStackWindowingMode);
754    }
755
756    /**
757     * Create a virtual display, launch a test activity there, destroy the display and check if test
758     * activity is moved to a stack on the default display.
759     */
760    private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int windowingMode)
761            throws Exception {
762        LogSeparator logSeparator;
763        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
764            // Create new virtual display.
765            final ActivityDisplay newDisplay = virtualDisplaySession
766                    .setPublicDisplay(true)
767                    .setLaunchInSplitScreen(splitScreen)
768                    .createDisplay();
769            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
770            if (splitScreen) {
771                mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
772            }
773
774            // Launch activity on new secondary display.
775            launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
776            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
777                    RESIZEABLE_ACTIVITY);
778            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
779            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
780
781            // Destroy virtual display.
782            logSeparator = separateLogs();
783        }
784
785        mAmWmState.computeState(true);
786        assertActivityLifecycle(RESIZEABLE_ACTIVITY, false /* relaunched */, logSeparator);
787        mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(RESIZEABLE_ACTIVITY)
788                .setWindowingMode(windowingMode)
789                .setActivityType(ACTIVITY_TYPE_STANDARD)
790                .build());
791        mAmWmState.assertSanity();
792        mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */);
793
794        // Check if the focus is switched back to primary display.
795        mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
796        mAmWmState.assertFocusedStack(
797                "Default stack on primary display must be focused after display removed",
798                windowingMode, ACTIVITY_TYPE_STANDARD);
799        mAmWmState.assertFocusedActivity(
800                "Focus must be switched back to activity on primary display",
801                RESIZEABLE_ACTIVITY);
802    }
803
804    /**
805     * Tests launching activities on secondary display and then removing it to see if stack focus
806     * is moved correctly.
807     */
808    @Test
809    public void testStackFocusSwitchOnStackEmptied() throws Exception {
810        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
811             final LockScreenSession lockScreenSession = new LockScreenSession()) {
812            // Create new virtual display.
813            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
814            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
815            final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
816
817            // Launch activity on new secondary display.
818            launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
819            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
820                    BROADCAST_RECEIVER_ACTIVITY);
821
822            // Lock the device, so that activity containers will be detached.
823            lockScreenSession.sleepDevice();
824
825            // Finish activity on secondary display.
826            executeShellCommand(FINISH_ACTIVITY_BROADCAST);
827
828            // Unlock and check if the focus is switched back to primary display.
829            lockScreenSession.wakeUpDevice()
830                    .unlockDevice();
831            mAmWmState.waitForFocusedStack(focusedStackId);
832            mAmWmState.waitForValidState(VIRTUAL_DISPLAY_ACTIVITY);
833            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
834            mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
835                    VIRTUAL_DISPLAY_ACTIVITY);
836        }
837    }
838
839    /**
840     * Tests that input events on the primary display take focus from the virtual display.
841     */
842    @Test
843    public void testStackFocusSwitchOnTouchEvent() throws Exception {
844        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
845            // Create new virtual display.
846            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
847
848            mAmWmState.computeState(VIRTUAL_DISPLAY_ACTIVITY);
849            mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
850                    VIRTUAL_DISPLAY_ACTIVITY);
851
852            launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
853
854            mAmWmState.computeState(TEST_ACTIVITY);
855            mAmWmState.assertFocusedActivity(
856                    "Activity launched on secondary display must be focused",
857                    TEST_ACTIVITY);
858
859            final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
860            final int width = displayMetrics.getSize().getWidth();
861            final int height = displayMetrics.getSize().getHeight();
862            executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
863
864            mAmWmState.computeState(VIRTUAL_DISPLAY_ACTIVITY);
865            mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
866                    VIRTUAL_DISPLAY_ACTIVITY);
867        }
868    }
869
870    /** Test that shell is allowed to launch on secondary displays. */
871    @Test
872    public void testPermissionLaunchFromShell() throws Exception {
873        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
874            // Create new virtual display.
875            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
876            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
877            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
878                    VIRTUAL_DISPLAY_ACTIVITY);
879            final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
880            ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
881                    defaultDisplayFocusedStackId);
882            assertEquals("Focus must remain on primary display",
883                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
884
885            // Launch activity on new secondary display.
886            launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
887            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
888                    TEST_ACTIVITY);
889            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
890            focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
891            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
892                    focusedStack.mDisplayId);
893
894            // Launch other activity with different uid and check it is launched on dynamic stack on
895            // secondary display.
896            final String startCmd = "am start -n " + getActivityName(SECOND_ACTIVITY)
897                            + " --display " + newDisplay.mId;
898            executeShellCommand(startCmd);
899
900            mAmWmState.waitForValidState(SECOND_ACTIVITY);
901            mAmWmState.assertFocusedActivity(
902                    "Focus must be on newly launched app", SECOND_ACTIVITY);
903            assertEquals("Activity launched by system must be on external display",
904                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
905        }
906    }
907
908    /** Test that launching from app that is on external display is allowed. */
909    @Test
910    public void testPermissionLaunchFromAppOnSecondary() throws Exception {
911        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
912            // Create new simulated display.
913            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
914                    .createDisplay();
915
916            // Launch activity with different uid on secondary display.
917            final String startCmd = "am start -n " + getActivityName(SECOND_ACTIVITY)
918                    + " --display " + newDisplay.mId;
919            executeShellCommand(startCmd);
920
921            mAmWmState.waitForValidState(SECOND_ACTIVITY);
922            mAmWmState.assertFocusedActivity(
923                    "Focus must be on newly launched app", SECOND_ACTIVITY);
924            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
925            ActivityManagerState.ActivityStack focusedStack =
926                    mAmWmState.getAmState().getStackById(externalFocusedStackId);
927            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
928                    focusedStack.mDisplayId);
929
930            // Launch another activity with third different uid from app on secondary display and
931            // check it is launched on secondary display.
932            getLaunchActivityBuilder()
933                    .setUseBroadcastReceiver(SECOND_LAUNCH_BROADCAST_RECEIVER,
934                            SECOND_LAUNCH_BROADCAST_ACTION)
935                    .setDisplayId(newDisplay.mId)
936                    .setTargetActivity(THIRD_ACTIVITY)
937                    .execute();
938
939            mAmWmState.waitForValidState(THIRD_ACTIVITY);
940            mAmWmState.assertFocusedActivity("Focus must be on newly launched app", THIRD_ACTIVITY);
941            assertEquals("Activity launched by app on secondary display must be on that display",
942                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
943        }
944    }
945
946    /** Tests that an activity can launch an activity from a different UID into its own task. */
947    @Test
948    public void testPermissionLaunchMultiUidTask() throws Exception {
949        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
950            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
951                    .createDisplay();
952
953            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
954            mAmWmState.computeState(LAUNCHING_ACTIVITY);
955
956            // Check that the first activity is launched onto the secondary display
957            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
958            ActivityManagerState.ActivityStack frontStack = mAmWmState.getAmState().getStackById(
959                    frontStackId);
960            assertEquals("Activity launched on secondary display must be resumed",
961                    getActivityName(LAUNCHING_ACTIVITY),
962                    frontStack.mResumedActivity);
963            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
964
965            // Launch an activity from a different UID into the first activity's task
966            getLaunchActivityBuilder().setTargetActivity(SECOND_ACTIVITY).execute();
967
968            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
969            frontStack = mAmWmState.getAmState().getStackById(frontStackId);
970            mAmWmState.assertFocusedActivity(
971                    "Focus must be on newly launched app", SECOND_ACTIVITY);
972            assertEquals("Secondary display must contain 1 task", 1, frontStack.getTasks().size());
973        }
974    }
975
976    /**
977     * Test that launching from display owner is allowed even when the the display owner
978     * doesn't have anything on the display.
979     */
980    @Test
981    public void testPermissionLaunchFromOwner() throws Exception {
982        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
983            // Create new virtual display.
984            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
985            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
986            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
987                    VIRTUAL_DISPLAY_ACTIVITY);
988            final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
989            ActivityManagerState.ActivityStack focusedStack =
990                    mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
991            assertEquals("Focus must remain on primary display",
992                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
993
994            // Launch other activity with different uid on secondary display.
995            final String startCmd = "am start -n " + getActivityName(SECOND_ACTIVITY)
996                    + " --display " + newDisplay.mId;
997            executeShellCommand(startCmd);
998
999            mAmWmState.waitForValidState(SECOND_ACTIVITY);
1000            mAmWmState.assertFocusedActivity(
1001                    "Focus must be on newly launched app", SECOND_ACTIVITY);
1002            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
1003            focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
1004            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
1005                    focusedStack.mDisplayId);
1006
1007            // Check that owner uid can launch its own activity on secondary display.
1008            getLaunchActivityBuilder()
1009                    .setUseBroadcastReceiver(LAUNCH_BROADCAST_RECEIVER, LAUNCH_BROADCAST_ACTION)
1010                    .setNewTask(true)
1011                    .setMultipleTask(true)
1012                    .setDisplayId(newDisplay.mId)
1013                    .execute();
1014
1015            mAmWmState.waitForValidState(TEST_ACTIVITY);
1016            mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
1017                    TEST_ACTIVITY);
1018            assertEquals("Activity launched by owner must be on external display",
1019                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
1020        }
1021    }
1022
1023    /**
1024     * Test that launching from app that is not present on external display and doesn't own it to
1025     * that external display is not allowed.
1026     */
1027    @Test
1028    public void testPermissionLaunchFromDifferentApp() throws Exception {
1029        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
1030            // Create new virtual display.
1031            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
1032            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
1033            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
1034                    VIRTUAL_DISPLAY_ACTIVITY);
1035            final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
1036            ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
1037                    defaultDisplayFocusedStackId);
1038            assertEquals("Focus must remain on primary display",
1039                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
1040
1041            // Launch activity on new secondary display.
1042            launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
1043            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
1044                    TEST_ACTIVITY);
1045            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
1046            focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
1047            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
1048                    focusedStack.mDisplayId);
1049
1050            final LogSeparator logSeparator = separateLogs();
1051
1052            // Launch other activity with different uid and check security exception is triggered.
1053            getLaunchActivityBuilder()
1054                    .setUseBroadcastReceiver(SECOND_LAUNCH_BROADCAST_RECEIVER,
1055                            SECOND_LAUNCH_BROADCAST_ACTION)
1056                    .setDisplayId(newDisplay.mId)
1057                    .setTargetActivity(THIRD_ACTIVITY)
1058                    .execute();
1059
1060            assertSecurityException("ActivityLauncher", logSeparator);
1061
1062            mAmWmState.waitForValidState(TEST_ACTIVITY);
1063            mAmWmState.assertFocusedActivity("Focus must be on first activity", TEST_ACTIVITY);
1064            assertEquals("Focused stack must be on secondary display's stack",
1065                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
1066        }
1067    }
1068
1069    private void assertSecurityException(String component, LogSeparator logSeparator)
1070            throws Exception {
1071        final Pattern pattern = Pattern.compile(".*SecurityException launching activity.*");
1072        for (int retry = 1; retry <= 5; retry++) {
1073            String[] logs = getDeviceLogsForComponents(logSeparator, component);
1074            for (String line : logs) {
1075                Matcher m = pattern.matcher(line);
1076                if (m.matches()) {
1077                    return;
1078                }
1079            }
1080            logAlways("***Waiting for SecurityException for " + component + " ... retry=" + retry);
1081            try {
1082                Thread.sleep(500);
1083            } catch (InterruptedException e) {
1084            }
1085        }
1086        fail("Expected exception for " + component + " not found");
1087    }
1088
1089    /**
1090     * Test that only private virtual display can show content with insecure keyguard.
1091     */
1092    @Test
1093    public void testFlagShowWithInsecureKeyguardOnPublicVirtualDisplay() throws Exception {
1094        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
1095            // Try to create new show-with-insecure-keyguard public virtual display.
1096            final ActivityDisplay newDisplay = virtualDisplaySession
1097                    .setPublicDisplay(true)
1098                    .setCanShowWithInsecureKeyguard(true)
1099                    .setMustBeCreated(false)
1100                    .createDisplay();
1101
1102            // Check that the display is not created.
1103            assertNull(newDisplay);
1104        }
1105    }
1106
1107    /**
1108     * Test that all activities that were on the private display are destroyed on display removal.
1109     */
1110    @Test
1111    public void testContentDestroyOnDisplayRemoved() throws Exception {
1112        LogSeparator logSeparator;
1113        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
1114            // Create new private virtual display.
1115            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
1116            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
1117
1118            // Launch activities on new secondary display.
1119            launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
1120            mAmWmState.assertVisibility(TEST_ACTIVITY, true /* visible */);
1121            mAmWmState.assertFocusedActivity("Launched activity must be focused",
1122                    TEST_ACTIVITY);
1123            launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
1124            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
1125            mAmWmState.assertFocusedActivity("Launched activity must be focused",
1126                    RESIZEABLE_ACTIVITY);
1127
1128            // Destroy the display and check if activities are removed from system.
1129            logSeparator = separateLogs();
1130        }
1131
1132        mAmWmState.waitForWithAmState(
1133                (state) -> !state.containsActivity(TEST_ACTIVITY)
1134                        && !state.containsActivity(RESIZEABLE_ACTIVITY),
1135                "Waiting for activity to be removed");
1136        mAmWmState.waitForWithWmState(
1137                (state) -> !state.containsWindow(getWindowName(TEST_ACTIVITY))
1138                        && !state.containsWindow(getWindowName(RESIZEABLE_ACTIVITY)),
1139                "Waiting for activity window to be gone");
1140
1141        // Check AM state.
1142        assertFalse("Activity from removed display must be destroyed",
1143                mAmWmState.getAmState().containsActivity(TEST_ACTIVITY));
1144        assertFalse("Activity from removed display must be destroyed",
1145                mAmWmState.getAmState().containsActivity(RESIZEABLE_ACTIVITY));
1146        // Check WM state.
1147        assertFalse("Activity windows from removed display must be destroyed",
1148                mAmWmState.getWmState().containsWindow(getWindowName(TEST_ACTIVITY)));
1149        assertFalse("Activity windows from removed display must be destroyed",
1150                mAmWmState.getWmState().containsWindow(getWindowName(RESIZEABLE_ACTIVITY)));
1151        // Check activity logs.
1152        assertActivityDestroyed(TEST_ACTIVITY, logSeparator);
1153        assertActivityDestroyed(RESIZEABLE_ACTIVITY, logSeparator);
1154    }
1155
1156    /**
1157     * Test that the update of display metrics updates all its content.
1158     */
1159    @Test
1160    public void testDisplayResize() throws Exception {
1161        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
1162            // Create new virtual display.
1163            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
1164            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
1165
1166            // Launch a resizeable activity on new secondary display.
1167            final LogSeparator initialLogSeparator = separateLogs();
1168            launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
1169            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
1170            mAmWmState.assertFocusedActivity("Launched activity must be focused",
1171                    RESIZEABLE_ACTIVITY);
1172
1173            // Grab reported sizes and compute new with slight size change.
1174            final ReportedSizes initialSize = getLastReportedSizesForActivity(
1175                    RESIZEABLE_ACTIVITY, initialLogSeparator);
1176
1177            // Resize the display
1178            final LogSeparator logSeparator = separateLogs();
1179            virtualDisplaySession.resizeDisplay();
1180
1181            mAmWmState.waitForWithAmState(amState -> {
1182                try {
1183                    return readConfigChangeNumber(RESIZEABLE_ACTIVITY, logSeparator) == 1
1184                            && amState.hasActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED);
1185                } catch (Exception e) {
1186                    logE("Error waiting for valid state: " + e.getMessage());
1187                    return false;
1188                }
1189            }, "Wait for the configuration change to happen and for activity to be resumed.");
1190
1191            mAmWmState.computeState(false /* compareTaskAndStackBounds */,
1192                    new WaitForValidActivityState(RESIZEABLE_ACTIVITY),
1193                    new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
1194            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
1195            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true);
1196
1197            // Check if activity in virtual display was resized properly.
1198            assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */,
1199                    1 /* numConfigChange */, logSeparator);
1200
1201            final ReportedSizes updatedSize = getLastReportedSizesForActivity(
1202                    RESIZEABLE_ACTIVITY, logSeparator);
1203            assertTrue(updatedSize.widthDp <= initialSize.widthDp);
1204            assertTrue(updatedSize.heightDp <= initialSize.heightDp);
1205            assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2);
1206            assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2);
1207        }
1208    }
1209
1210    /** Read the number of configuration changes sent to activity from logs. */
1211    private int readConfigChangeNumber(ComponentName activityName, LogSeparator logSeparator)
1212            throws Exception {
1213        return (new ActivityLifecycleCounts(activityName, logSeparator)).mConfigurationChangedCount;
1214    }
1215
1216    /**
1217     * Tests that when an activity is launched with displayId specified and there is an existing
1218     * matching task on some other display - that task will moved to the target display.
1219     */
1220    @Test
1221    public void testMoveToDisplayOnLaunch() throws Exception {
1222        // Launch activity with unique affinity, so it will the only one in its task.
1223        launchActivity(LAUNCHING_ACTIVITY);
1224
1225        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
1226            // Create new virtual display.
1227            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
1228            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
1229            // Launch something to that display so that a new stack is created. We need this to be
1230            // able to compare task numbers in stacks later.
1231            launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
1232            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
1233
1234            final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY)
1235                    .mStacks.size();
1236            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
1237            final int taskNumOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
1238                    .getTasks().size();
1239
1240            // Launch activity on new secondary display.
1241            // Using custom command here, because normally we add flags
1242            // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}
1243            // when launching on some specific display. We don't do it here as we want an existing
1244            // task to be used.
1245            final String launchCommand = "am start -n " + getActivityName(LAUNCHING_ACTIVITY)
1246                    + " --display " + newDisplay.mId;
1247            executeShellCommand(launchCommand);
1248            mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED);
1249
1250            // Check that activity is brought to front.
1251            mAmWmState.assertFocusedActivity("Existing task must be brought to front",
1252                    LAUNCHING_ACTIVITY);
1253            mAmWmState.assertResumedActivity("Existing task must be resumed",
1254                    LAUNCHING_ACTIVITY);
1255
1256            // Check that activity is on the right display.
1257            final ActivityManagerState.ActivityStack firstFrontStack =
1258                    mAmWmState.getAmState().getStackById(frontStackId);
1259            assertEquals("Activity must be moved to the secondary display",
1260                    getActivityName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
1261            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
1262
1263            // Check that task has moved from primary display to secondary.
1264            final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY)
1265                    .mStacks.size();
1266            assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
1267                    stackNumFinal);
1268            final int taskNumFinalOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
1269                    .getTasks().size();
1270            assertEquals("Task number in stack on external display must be incremented.",
1271                    taskNumOnSecondary + 1, taskNumFinalOnSecondary);
1272        }
1273    }
1274
1275    /**
1276     * Tests that when an activity is launched with displayId specified and there is an existing
1277     * matching task on some other display - that task will moved to the target display.
1278     */
1279    @Test
1280    public void testMoveToEmptyDisplayOnLaunch() throws Exception {
1281        // Launch activity with unique affinity, so it will the only one in its task.
1282        launchActivity(LAUNCHING_ACTIVITY);
1283
1284        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
1285            // Create new virtual display.
1286            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
1287            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
1288
1289            final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY).mStacks.size();
1290
1291            // Launch activity on new secondary display.
1292            // Using custom command here, because normally we add flags
1293            // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}
1294            // when launching on some specific display. We don't do it here as we want an existing
1295            // task to be used.
1296            final String launchCommand = "am start -n " + getActivityName(LAUNCHING_ACTIVITY)
1297                    + " --display " + newDisplay.mId;
1298            executeShellCommand(launchCommand);
1299            mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED);
1300
1301            // Check that activity is brought to front.
1302            mAmWmState.assertFocusedActivity("Existing task must be brought to front",
1303                    LAUNCHING_ACTIVITY);
1304            mAmWmState.assertResumedActivity("Existing task must be resumed",
1305                    LAUNCHING_ACTIVITY);
1306
1307            // Check that activity is on the right display.
1308            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
1309            final ActivityManagerState.ActivityStack firstFrontStack =
1310                    mAmWmState.getAmState().getStackById(frontStackId);
1311            assertEquals("Activity must be moved to the secondary display",
1312                    getActivityName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
1313            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
1314
1315            // Check that task has moved from primary display to secondary.
1316            final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY)
1317                    .mStacks.size();
1318            assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
1319                    stackNumFinal);
1320        }
1321    }
1322
1323    /**
1324     * Tests that when primary display is rotated secondary displays are not affected.
1325     */
1326    @Test
1327    public void testRotationNotAffectingSecondaryScreen() throws Exception {
1328        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
1329            // Create new virtual display.
1330            final ActivityDisplay newDisplay = virtualDisplaySession.setResizeDisplay(false)
1331                    .createDisplay();
1332            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
1333
1334            // Launch activity on new secondary display.
1335            LogSeparator logSeparator = separateLogs();
1336            launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
1337            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
1338                    RESIZEABLE_ACTIVITY);
1339            final ReportedSizes initialSizes = getLastReportedSizesForActivity(
1340                    RESIZEABLE_ACTIVITY, logSeparator);
1341            assertNotNull("Test activity must have reported initial sizes on launch", initialSizes);
1342
1343            try (final RotationSession rotationSession = new RotationSession()) {
1344                // Rotate primary display and check that activity on secondary display is not
1345                // affected.
1346
1347                rotateAndCheckSameSizes(rotationSession, RESIZEABLE_ACTIVITY);
1348
1349                // Launch activity to secondary display when primary one is rotated.
1350                final int initialRotation = mAmWmState.getWmState().getRotation();
1351                rotationSession.set((initialRotation + 1) % 4);
1352
1353                logSeparator = separateLogs();
1354                launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
1355                mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
1356                mAmWmState.assertFocusedActivity("Focus must be on secondary display",
1357                        TEST_ACTIVITY);
1358                final ReportedSizes testActivitySizes = getLastReportedSizesForActivity(
1359                        TEST_ACTIVITY, logSeparator);
1360                assertEquals(
1361                        "Sizes of secondary display must not change after rotation of primary "
1362                                + "display",
1363                        initialSizes, testActivitySizes);
1364            }
1365        }
1366    }
1367
1368    private void rotateAndCheckSameSizes(
1369            RotationSession rotationSession, ComponentName activityName) throws Exception {
1370        for (int rotation = 3; rotation >= 0; --rotation) {
1371            final LogSeparator logSeparator = separateLogs();
1372            rotationSession.set(rotation);
1373            final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName,
1374                    logSeparator);
1375            assertNull("Sizes must not change after rotation", rotatedSizes);
1376        }
1377    }
1378
1379    /**
1380     * Tests that task affinity does affect what display an activity is launched on but that
1381     * matching the task component root does.
1382     */
1383    @Test
1384    public void testTaskMatchAcrossDisplays() throws Exception {
1385        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
1386            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
1387
1388            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
1389            mAmWmState.computeState(LAUNCHING_ACTIVITY);
1390
1391            // Check that activity is on the secondary display.
1392            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
1393            final ActivityManagerState.ActivityStack firstFrontStack =
1394                    mAmWmState.getAmState().getStackById(frontStackId);
1395            assertEquals("Activity launched on secondary display must be resumed",
1396                    getActivityName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
1397            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
1398
1399            executeShellCommand("am start -n " + getActivityName(ALT_LAUNCHING_ACTIVITY));
1400            mAmWmState.waitForValidState(ALT_LAUNCHING_ACTIVITY);
1401
1402            // Check that second activity gets launched on the default display despite
1403            // the affinity match on the secondary display.
1404            final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId(
1405                    DEFAULT_DISPLAY);
1406            final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
1407                    mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId);
1408            assertEquals("Activity launched on default display must be resumed",
1409                    getActivityName(ALT_LAUNCHING_ACTIVITY),
1410                    defaultDisplayFrontStack.mResumedActivity);
1411            mAmWmState.assertFocusedStack("Focus must be on primary display",
1412                    defaultDisplayFrontStackId);
1413
1414            executeShellCommand("am start -n " + getActivityName(LAUNCHING_ACTIVITY));
1415            mAmWmState.waitForFocusedStack(frontStackId);
1416
1417            // Check that the third intent is redirected to the first task due to the root
1418            // component match on the secondary display.
1419            final ActivityManagerState.ActivityStack secondFrontStack =
1420                    mAmWmState.getAmState().getStackById(frontStackId);
1421            assertEquals("Activity launched on secondary display must be resumed",
1422                    getActivityName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity);
1423            mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
1424            assertEquals("Focused stack must only contain 1 task",
1425                    1, secondFrontStack.getTasks().size());
1426            assertEquals("Focused task must only contain 1 activity",
1427                    1, secondFrontStack.getTasks().get(0).mActivities.size());
1428        }
1429    }
1430
1431    /**
1432     * Tests that the task affinity search respects the launch display id.
1433     */
1434    @Test
1435    public void testLaunchDisplayAffinityMatch() throws Exception {
1436        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
1437            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
1438
1439            launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
1440
1441            // Check that activity is on the secondary display.
1442            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
1443            final ActivityManagerState.ActivityStack firstFrontStack =
1444                    mAmWmState.getAmState().getStackById(frontStackId);
1445            assertEquals("Activity launched on secondary display must be resumed",
1446                    getActivityName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
1447            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
1448
1449            // We don't want FLAG_ACTIVITY_MULTIPLE_TASK, so we can't use launchActivityOnDisplay
1450            executeShellCommand("am start -n " + getActivityName(ALT_LAUNCHING_ACTIVITY)
1451                    + " -f 0x10000000" // FLAG_ACTIVITY_NEW_TASK
1452                    + " --display " + newDisplay.mId);
1453            mAmWmState.computeState(ALT_LAUNCHING_ACTIVITY);
1454
1455            // Check that second activity gets launched into the affinity matching
1456            // task on the secondary display
1457            final int secondFrontStackId =
1458                    mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
1459            final ActivityManagerState.ActivityStack secondFrontStack =
1460                    mAmWmState.getAmState().getStackById(secondFrontStackId);
1461            assertEquals("Activity launched on secondary display must be resumed",
1462                    getActivityName(ALT_LAUNCHING_ACTIVITY),
1463                    secondFrontStack.mResumedActivity);
1464            mAmWmState.assertFocusedStack("Focus must be on secondary display",
1465                    secondFrontStackId);
1466            assertEquals("Focused stack must only contain 1 task",
1467                    1, secondFrontStack.getTasks().size());
1468            assertEquals("Focused task must contain 2 activities",
1469                    2, secondFrontStack.getTasks().get(0).mActivities.size());
1470        }
1471    }
1472
1473    /**
1474     * Tests than a new task launched by an activity will end up on that activity's display
1475     * even if the focused stack is not on that activity's display.
1476     */
1477    @Test
1478    public void testNewTaskSameDisplay() throws Exception {
1479        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
1480            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
1481                    .createDisplay();
1482
1483            launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
1484            mAmWmState.computeState(BROADCAST_RECEIVER_ACTIVITY);
1485
1486            // Check that the first activity is launched onto the secondary display
1487            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
1488            final ActivityManagerState.ActivityStack firstFrontStack =
1489                    mAmWmState.getAmState().getStackById(frontStackId);
1490            assertEquals("Activity launched on secondary display must be resumed",
1491                    getActivityName(BROADCAST_RECEIVER_ACTIVITY),
1492                    firstFrontStack.mResumedActivity);
1493            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
1494
1495            executeShellCommand("am start -n " + getActivityName(TEST_ACTIVITY));
1496            mAmWmState.waitForValidState(TEST_ACTIVITY);
1497
1498            // Check that the second activity is launched on the default display
1499            final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
1500            final ActivityManagerState.ActivityStack focusedStack =
1501                    mAmWmState.getAmState().getStackById(focusedStackId);
1502            assertEquals("Activity launched on default display must be resumed",
1503                    getActivityName(TEST_ACTIVITY), focusedStack.mResumedActivity);
1504            assertEquals("Focus must be on primary display",
1505                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
1506
1507            executeShellCommand(LAUNCH_ACTIVITY_BROADCAST + getActivityName(LAUNCHING_ACTIVITY));
1508
1509            // Check that the third activity ends up in a new task in the same stack as the
1510            // first activity
1511            mAmWmState.waitForValidState(LAUNCHING_ACTIVITY);
1512            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
1513            final ActivityManagerState.ActivityStack secondFrontStack =
1514                    mAmWmState.getAmState().getStackById(frontStackId);
1515            assertEquals("Activity must be launched on secondary display",
1516                    getActivityName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity);
1517            assertEquals("Secondary display must contain 2 tasks",
1518                    2, secondFrontStack.getTasks().size());
1519        }
1520    }
1521
1522    /**
1523     * Tests than an immediate launch after new display creation is handled correctly.
1524     */
1525    @Test
1526    public void testImmediateLaunchOnNewDisplay() throws Exception {
1527        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
1528            // Create new virtual display and immediately launch an activity on it.
1529            final ActivityDisplay newDisplay = virtualDisplaySession
1530                    .setLaunchActivity(TEST_ACTIVITY)
1531                    .createDisplay();
1532
1533            // Check that activity is launched and placed correctly.
1534            mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
1535            mAmWmState.assertResumedActivity("Test activity must be launched on a new display",
1536                    TEST_ACTIVITY);
1537            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
1538            final ActivityManagerState.ActivityStack firstFrontStack =
1539                    mAmWmState.getAmState().getStackById(frontStackId);
1540            assertEquals("Activity launched on secondary display must be resumed",
1541                    getActivityName(TEST_ACTIVITY), firstFrontStack.mResumedActivity);
1542            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
1543        }
1544    }
1545
1546    /**
1547     * Tests that turning the primary display off does not affect the activity running
1548     * on an external secondary display.
1549     */
1550    @Test
1551    public void testExternalDisplayActivityTurnPrimaryOff() throws Exception {
1552        // Launch something on the primary display so we know there is a resumed activity there
1553        launchActivity(RESIZEABLE_ACTIVITY);
1554        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
1555                "Activity launched on primary display must be resumed");
1556
1557        try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
1558             final PrimaryDisplayStateSession displayStateSession =
1559                     new PrimaryDisplayStateSession()) {
1560            final ActivityDisplay newDisplay =
1561                    externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
1562
1563            launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
1564
1565            // Check that the activity is launched onto the external display
1566            waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId,
1567                    "Activity launched on external display must be resumed");
1568
1569            final LogSeparator logSeparator = separateLogs();
1570
1571            displayStateSession.turnScreenOff();
1572
1573            // Wait for the fullscreen stack to start sleeping, and then make sure the
1574            // test activity is still resumed.
1575            int retry = 0;
1576            ActivityLifecycleCounts lifecycleCounts;
1577            do {
1578                lifecycleCounts = new ActivityLifecycleCounts(RESIZEABLE_ACTIVITY, logSeparator);
1579                if (lifecycleCounts.mStopCount == 1) {
1580                    break;
1581                }
1582                logAlways("***testExternalDisplayActivityTurnPrimaryOff... retry=" + retry);
1583                SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
1584            } while (retry++ < 5);
1585
1586            if (lifecycleCounts.mStopCount != 1) {
1587                fail(RESIZEABLE_ACTIVITY + " has received " + lifecycleCounts.mStopCount
1588                        + " onStop() calls, expecting 1");
1589            }
1590            waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId,
1591                    "Activity launched on external display must be resumed");
1592        }
1593    }
1594
1595    /**
1596     * Tests that an activity can be launched on a secondary display while the primary
1597     * display is off.
1598     */
1599    @Test
1600    public void testLaunchExternalDisplayActivityWhilePrimaryOff() throws Exception {
1601        // Launch something on the primary display so we know there is a resumed activity there
1602        launchActivity(RESIZEABLE_ACTIVITY);
1603        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
1604                "Activity launched on primary display must be resumed");
1605
1606        try (final PrimaryDisplayStateSession displayStateSession =
1607                     new PrimaryDisplayStateSession();
1608             final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
1609            displayStateSession.turnScreenOff();
1610
1611            // Make sure there is no resumed activity when the primary display is off
1612            waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY,
1613                    "Activity launched on primary display must be stopped after turning off");
1614            assertEquals("Unexpected resumed activity",
1615                    0, mAmWmState.getAmState().getResumedActivitiesCount());
1616
1617            final ActivityDisplay newDisplay =
1618                    externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
1619
1620            launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
1621
1622            // Check that the test activity is resumed on the external display
1623            waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId,
1624                    "Activity launched on external display must be resumed");
1625        }
1626    }
1627
1628    /**
1629     * Tests that turning the secondary display off stops activities running on that display.
1630     */
1631    @Test
1632    public void testExternalDisplayToggleState() throws Exception {
1633        try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
1634            final ActivityDisplay newDisplay =
1635                    externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */);
1636
1637            launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
1638
1639            // Check that the test activity is resumed on the external display
1640            waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId,
1641                    "Activity launched on external display must be resumed");
1642
1643            externalDisplaySession.turnDisplayOff();
1644
1645            // Check that turning off the external display stops the activity
1646            waitAndAssertActivityStopped(TEST_ACTIVITY,
1647                    "Activity launched on external display must be stopped after turning off");
1648
1649            externalDisplaySession.turnDisplayOn();
1650
1651            // Check that turning on the external display resumes the activity
1652            waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId,
1653                    "Activity launched on external display must be resumed");
1654        }
1655    }
1656
1657    /**
1658     * Tests that tapping on the primary display after showing the keyguard resumes the
1659     * activity on the primary display.
1660     */
1661    @Test
1662    public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
1663        // Launch something on the primary display so we know there is a resumed activity there
1664        launchActivity(RESIZEABLE_ACTIVITY);
1665        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
1666                "Activity launched on primary display must be resumed");
1667
1668        try (final LockScreenSession lockScreenSession = new LockScreenSession();
1669             final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
1670            lockScreenSession.sleepDevice();
1671
1672            // Make sure there is no resumed activity when the primary display is off
1673            waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY,
1674                    "Activity launched on primary display must be stopped after turning off");
1675            assertEquals("Unexpected resumed activity",
1676                    0, mAmWmState.getAmState().getResumedActivitiesCount());
1677
1678            final ActivityDisplay newDisplay =
1679                    externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
1680
1681            launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
1682
1683            // Check that the test activity is resumed on the external display
1684            waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId,
1685                    "Activity launched on external display must be resumed");
1686
1687            // Unlock the device and tap on the middle of the primary display
1688            lockScreenSession.wakeUpDevice();
1689            executeShellCommand("wm dismiss-keyguard");
1690            mAmWmState.waitForKeyguardGone();
1691            mAmWmState.waitForValidState(TEST_ACTIVITY);
1692            final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
1693            final int width = displayMetrics.getSize().getWidth();
1694            final int height = displayMetrics.getSize().getHeight();
1695            executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
1696
1697            // Check that the activity on the primary display is resumed
1698            waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
1699                    "Activity launched on primary display must be resumed");
1700            assertEquals("Unexpected resumed activity",
1701                    1, mAmWmState.getAmState().getResumedActivitiesCount());
1702        }
1703    }
1704
1705    private void waitAndAssertActivityResumed(
1706            ComponentName activityName, int displayId, String message) throws Exception {
1707        mAmWmState.waitForActivityState(activityName, STATE_RESUMED);
1708
1709        assertEquals(message,
1710                getActivityName(activityName), mAmWmState.getAmState().getResumedActivity());
1711        final int frontStackId = mAmWmState.getAmState().getFrontStackId(displayId);
1712        ActivityManagerState.ActivityStack firstFrontStack =
1713                mAmWmState.getAmState().getStackById(frontStackId);
1714        assertEquals(message,
1715                getActivityName(activityName), firstFrontStack.mResumedActivity);
1716        assertTrue(message,
1717                mAmWmState.getAmState().hasActivityState(activityName, STATE_RESUMED));
1718        mAmWmState.assertFocusedStack("Focus must be on external display", frontStackId);
1719        mAmWmState.assertVisibility(activityName, true /* visible */);
1720    }
1721
1722    private void waitAndAssertActivityStopped(ComponentName activityName, String message)
1723            throws Exception {
1724        mAmWmState.waitForActivityState(activityName, STATE_STOPPED);
1725
1726        assertTrue(message, mAmWmState.getAmState().hasActivityState(activityName,
1727                STATE_STOPPED));
1728    }
1729
1730    /**
1731     * Tests that showWhenLocked works on a secondary display.
1732     */
1733    @Test
1734    public void testSecondaryDisplayShowWhenLocked() throws Exception {
1735        try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
1736             final LockScreenSession lockScreenSession = new LockScreenSession()) {
1737            lockScreenSession.setLockCredential();
1738
1739            launchActivity(TEST_ACTIVITY);
1740
1741            final ActivityDisplay newDisplay =
1742                    externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */);
1743            launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, newDisplay.mId);
1744
1745            lockScreenSession.gotoKeyguard();
1746
1747            mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_STOPPED);
1748            mAmWmState.waitForActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, STATE_RESUMED);
1749
1750            mAmWmState.computeState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY);
1751            assertTrue("Expected resumed activity on secondary display", mAmWmState.getAmState()
1752                    .hasActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, STATE_RESUMED));
1753        }
1754    }
1755
1756    /** Assert that component received onMovedToDisplay and onConfigurationChanged callbacks. */
1757    private void assertMovedToDisplay(ComponentName componentName, LogSeparator logSeparator)
1758            throws Exception {
1759        final ActivityLifecycleCounts lifecycleCounts =
1760                new ActivityLifecycleCounts(componentName, logSeparator);
1761        if (lifecycleCounts.mDestroyCount != 0) {
1762            fail(componentName + " has been destroyed " + lifecycleCounts.mDestroyCount
1763                    + " time(s), wasn't expecting any");
1764        } else if (lifecycleCounts.mCreateCount != 0) {
1765            fail(componentName + " has been (re)created " + lifecycleCounts.mCreateCount
1766                    + " time(s), wasn't expecting any");
1767        } else if (lifecycleCounts.mConfigurationChangedCount != 1) {
1768            fail(componentName + " has received "
1769                    + lifecycleCounts.mConfigurationChangedCount
1770                    + " onConfigurationChanged() calls, expecting " + 1);
1771        } else if (lifecycleCounts.mMovedToDisplayCount != 1) {
1772            fail(componentName + " has received "
1773                    + lifecycleCounts.mMovedToDisplayCount
1774                    + " onMovedToDisplay() calls, expecting " + 1);
1775        }
1776    }
1777
1778    private class ExternalDisplaySession implements AutoCloseable {
1779
1780        @Nullable
1781        private VirtualDisplayHelper mExternalDisplayHelper;
1782
1783        /**
1784         * Creates a private virtual display with the external and show with insecure
1785         * keyguard flags set.
1786         */
1787        ActivityDisplay createVirtualDisplay(boolean showContentWhenLocked)
1788                throws Exception {
1789            final List<ActivityDisplay> originalDS = getDisplaysStates();
1790            final int originalDisplayCount = originalDS.size();
1791
1792            mExternalDisplayHelper = new VirtualDisplayHelper();
1793            mExternalDisplayHelper.createAndWaitForDisplay(showContentWhenLocked);
1794
1795            // Wait for the virtual display to be created and get configurations.
1796            final List<ActivityDisplay> ds = getDisplayStateAfterChange(originalDisplayCount + 1);
1797            assertEquals("New virtual display must be created", originalDisplayCount + 1,
1798                    ds.size());
1799
1800            // Find the newly added display.
1801            final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
1802            return newDisplays.get(0);
1803        }
1804
1805        void turnDisplayOff() {
1806            if (mExternalDisplayHelper == null) {
1807                throw new RuntimeException("No external display created");
1808            }
1809            mExternalDisplayHelper.turnDisplayOff();
1810        }
1811
1812        void turnDisplayOn() {
1813            if (mExternalDisplayHelper == null) {
1814                throw new RuntimeException("No external display created");
1815            }
1816            mExternalDisplayHelper.turnDisplayOn();
1817        }
1818
1819        @Override
1820        public void close() throws Exception {
1821            if (mExternalDisplayHelper != null) {
1822                mExternalDisplayHelper.releaseDisplay();
1823                mExternalDisplayHelper = null;
1824            }
1825        }
1826    }
1827
1828    private static class PrimaryDisplayStateSession implements AutoCloseable {
1829
1830        void turnScreenOff() {
1831            setPrimaryDisplayState(false);
1832        }
1833
1834        @Override
1835        public void close() throws Exception {
1836            setPrimaryDisplayState(true);
1837        }
1838
1839        /** Turns the primary display on/off by pressing the power key */
1840        private void setPrimaryDisplayState(boolean wantOn) {
1841            if (wantOn) {
1842                pressWakeupButton();
1843            } else {
1844                pressSleepButton();
1845            }
1846            VirtualDisplayHelper.waitForDefaultDisplayState(wantOn);
1847        }
1848    }
1849}
1850