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 package android.server.cts;
17 
18 import android.platform.test.annotations.Presubmit;
19 
20 import com.android.tradefed.device.CollectingOutputReceiver;
21 import com.android.tradefed.device.DeviceNotAvailableException;
22 
23 import java.util.Collections;
24 import java.util.LinkedList;
25 import java.util.List;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28 
29 import static android.server.cts.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
30 import static android.server.cts.ActivityManagerState.STATE_RESUMED;
31 import static android.server.cts.StateLogger.log;
32 import static android.server.cts.StateLogger.logE;
33 
34 /**
35  * Build: mmma -j32 cts/hostsidetests/services
36  * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerDisplayTests
37  */
38 public class ActivityManagerDisplayTests extends ActivityManagerDisplayTestBase {
39     private static final String WM_SIZE = "wm size";
40     private static final String WM_DENSITY = "wm density";
41 
42     private static final String TEST_ACTIVITY_NAME = "TestActivity";
43     private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
44     private static final String RESIZEABLE_ACTIVITY_NAME = "ResizeableActivity";
45     private static final String NON_RESIZEABLE_ACTIVITY_NAME = "NonResizeableActivity";
46     private static final String SECOND_ACTIVITY_NAME = "SecondActivity";
47     private static final String THIRD_ACTIVITY_NAME = "ThirdActivity";
48     private static final String VR_TEST_ACTIVITY_NAME = "VrTestActivity";
49     private static final String SECOND_PACKAGE_NAME = "android.server.cts.second";
50     private static final String THIRD_PACKAGE_NAME = "android.server.cts.third";
51     private static final int VR_VIRTUAL_DISPLAY_WIDTH = 70;
52     private static final int VR_VIRTUAL_DISPLAY_HEIGHT = 90;
53     private static final int VR_VIRTUAL_DISPLAY_DPI = 320;
54 
55     /** Physical display metrics and overrides in the beginning of the test. */
56     private ReportedDisplayMetrics mInitialDisplayMetrics;
57 
58     @Override
setUp()59     protected void setUp() throws Exception {
60         super.setUp();
61         mInitialDisplayMetrics = getDisplayMetrics();
62     }
63 
64     @Override
tearDown()65     protected void tearDown() throws Exception {
66         try {
67             enablePersistentVrMode(false);
68             restoreDisplayMetricsOverrides();
69         } catch (DeviceNotAvailableException e) {
70             logE(e.getMessage());
71         }
72         super.tearDown();
73     }
74 
enablePersistentVrMode(boolean enabled)75     private void enablePersistentVrMode(boolean enabled) throws Exception {
76         if (enabled) {
77             executeShellCommand("setprop vr_virtualdisplay true");
78             executeShellCommand("vr set-persistent-vr-mode-enabled true");
79         } else {
80             executeShellCommand("vr set-persistent-vr-mode-enabled false");
81             executeShellCommand("setprop vr_virtualdisplay false");
82         }
83     }
84 
restoreDisplayMetricsOverrides()85     private void restoreDisplayMetricsOverrides() throws Exception {
86         if (mInitialDisplayMetrics.sizeOverrideSet) {
87             executeShellCommand(WM_SIZE + " " + mInitialDisplayMetrics.overrideWidth + "x"
88                     + mInitialDisplayMetrics.overrideHeight);
89         } else {
90             executeShellCommand("wm size reset");
91         }
92         if (mInitialDisplayMetrics.densityOverrideSet) {
93             executeShellCommand(WM_DENSITY + " " + mInitialDisplayMetrics.overrideDensity);
94         } else {
95             executeShellCommand("wm density reset");
96         }
97     }
98 
99     /**
100      * Tests that the global configuration is equal to the default display's override configuration.
101      */
testDefaultDisplayOverrideConfiguration()102     public void testDefaultDisplayOverrideConfiguration() throws Exception {
103         final ReportedDisplays reportedDisplays = getDisplaysStates();
104         assertNotNull("Global configuration must not be empty.", reportedDisplays.mGlobalConfig);
105         final DisplayState primaryDisplay = reportedDisplays.getDisplayState(DEFAULT_DISPLAY_ID);
106         assertEquals("Primary display's configuration should not be equal to global configuration.",
107                 reportedDisplays.mGlobalConfig, primaryDisplay.mOverrideConfig);
108     }
109 
110     /**
111      * Tests that secondary display has override configuration set.
112      */
testCreateVirtualDisplayWithCustomConfig()113     public void testCreateVirtualDisplayWithCustomConfig() throws Exception {
114         // Create new virtual display.
115         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
116 
117         // Find the density of created display.
118         final int newDensityDpi = newDisplay.getDpi();
119         assertEquals(CUSTOM_DENSITY_DPI, newDensityDpi);
120     }
121 
122     /**
123      * Tests that launch on secondary display is not permitted if device has the feature disabled.
124      * Activities requested to be launched on a secondary display in this case should land on the
125      * default display.
126      */
testMultiDisplayDisabled()127     public void testMultiDisplayDisabled() throws Exception {
128         if (supportsMultiDisplay()) {
129             // Only check devices with the feature disabled.
130             return;
131         }
132 
133         // Create new virtual display.
134         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
135 
136         // Launch activity on new secondary display.
137         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
138         mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
139 
140         mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
141 
142         // Check that activity is on the right display.
143         final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
144         final ActivityManagerState.ActivityStack frontStack
145                 = mAmWmState.getAmState().getStackById(frontStackId);
146         assertEquals("Launched activity must be resumed",
147                 getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
148         assertEquals("Front stack must be on the default display", DEFAULT_DISPLAY_ID,
149                 frontStack.mDisplayId);
150         mAmWmState.assertFocusedStack("Focus must be on the default display", frontStackId);
151     }
152 
153     /**
154      * Tests that any new activity launch in Vr mode is in Vr display.
155      */
testVrActivityLaunch()156     public void testVrActivityLaunch() throws Exception {
157         if (!supportsVrMode() || !supportsMultiDisplay()) {
158             // VR Mode is not supported on this device, bail from this test.
159             return;
160         }
161 
162         // Put the device in persistent vr mode.
163         enablePersistentVrMode(true);
164 
165         // Launch the VR activity.
166         launchActivity(VR_TEST_ACTIVITY_NAME);
167         mAmWmState.computeState(mDevice, new String[] {VR_TEST_ACTIVITY_NAME});
168         mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
169 
170         // Launch the non-VR 2D activity and check where it ends up.
171         launchActivity(LAUNCHING_ACTIVITY);
172         mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
173 
174         // Ensure that the subsequent activity is visible
175         mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
176 
177         // Check that activity is launched in focused stack on primary display.
178         mAmWmState.assertFocusedActivity("Launched activity must be focused", LAUNCHING_ACTIVITY);
179         final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
180         final ActivityManagerState.ActivityStack focusedStack
181                 = mAmWmState.getAmState().getStackById(focusedStackId);
182         assertEquals("Launched activity must be resumed in focused stack",
183             getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
184 
185         // Check if the launch activity is in Vr virtual display id.
186         final ReportedDisplays reportedDisplays = getDisplaysStates();
187         assertNotNull("Global configuration must not be empty.", reportedDisplays.mGlobalConfig);
188         final DisplayState vrDisplay = reportedDisplays.getDisplayState(VR_VIRTUAL_DISPLAY_WIDTH,
189             VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
190         assertNotNull("Vr mode should have a virtual display", vrDisplay);
191 
192         // Check if the focused activity is on this virtual stack.
193         assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mDisplayId,
194             focusedStack.mDisplayId);
195 
196         // Put the device out of persistent vr mode.
197         enablePersistentVrMode(false);
198     }
199 
200     /**
201      * Tests that any activity already present is re-launched in Vr display in vr mode.
202      */
testVrActivityReLaunch()203     public void testVrActivityReLaunch() throws Exception {
204         if (!supportsVrMode() || !supportsMultiDisplay()) {
205             // VR Mode is not supported on this device, bail from this test.
206             return;
207         }
208 
209         // Launch a 2D activity.
210         launchActivity(LAUNCHING_ACTIVITY);
211 
212         // Put the device in persistent vr mode.
213         enablePersistentVrMode(true);
214 
215         // Launch the VR activity.
216         launchActivity(VR_TEST_ACTIVITY_NAME);
217         mAmWmState.computeState(mDevice, new String[] {VR_TEST_ACTIVITY_NAME});
218         mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
219 
220         // Re-launch the non-VR 2D activity and check where it ends up.
221         launchActivity(LAUNCHING_ACTIVITY);
222         mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
223 
224         // Ensure that the subsequent activity is visible
225         mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
226 
227         // Check that activity is launched in focused stack on primary display.
228         mAmWmState.assertFocusedActivity("Launched activity must be focused", LAUNCHING_ACTIVITY);
229         final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
230         final ActivityManagerState.ActivityStack focusedStack
231                 = mAmWmState.getAmState().getStackById(focusedStackId);
232         assertEquals("Launched activity must be resumed in focused stack",
233             getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
234 
235         // Check if the launch activity is in Vr virtual display id.
236         final ReportedDisplays reportedDisplays = getDisplaysStates();
237         assertNotNull("Global configuration must not be empty.", reportedDisplays.mGlobalConfig);
238         final DisplayState vrDisplay = reportedDisplays.getDisplayState(VR_VIRTUAL_DISPLAY_WIDTH,
239             VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
240         assertNotNull("Vr mode should have a virtual display", vrDisplay);
241 
242         // Check if the focused activity is on this virtual stack.
243         assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mDisplayId,
244             focusedStack.mDisplayId);
245 
246         // Put the device out of persistent vr mode.
247         enablePersistentVrMode(false);
248     }
249 
250     /**
251      * Tests that any new activity launch post Vr mode is in the main display.
252      */
testActivityLaunchPostVr()253     public void testActivityLaunchPostVr() throws Exception {
254         if (!supportsVrMode() || !supportsMultiDisplay()) {
255             // VR Mode is not supported on this device, bail from this test.
256             return;
257         }
258 
259         // Put the device in persistent vr mode.
260         enablePersistentVrMode(true);
261 
262         // Launch the VR activity.
263         launchActivity(VR_TEST_ACTIVITY_NAME);
264         mAmWmState.computeState(mDevice, new String[] {VR_TEST_ACTIVITY_NAME});
265         mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
266 
267         // Launch the non-VR 2D activity and check where it ends up.
268         launchActivity(ALT_LAUNCHING_ACTIVITY);
269         mAmWmState.computeState(mDevice, new String[] {ALT_LAUNCHING_ACTIVITY});
270 
271         // Ensure that the subsequent activity is visible
272         mAmWmState.assertVisibility(ALT_LAUNCHING_ACTIVITY, true /* visible */);
273 
274         // Check that activity is launched in focused stack on primary display.
275         mAmWmState.assertFocusedActivity("Launched activity must be focused", ALT_LAUNCHING_ACTIVITY);
276         final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
277         final ActivityManagerState.ActivityStack focusedStack
278                 = mAmWmState.getAmState().getStackById(focusedStackId);
279         assertEquals("Launched activity must be resumed in focused stack",
280             getActivityComponentName(ALT_LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
281 
282         // Check if the launch activity is in Vr virtual display id.
283         final ReportedDisplays reportedDisplays = getDisplaysStates();
284         assertNotNull("Global configuration must not be empty.", reportedDisplays.mGlobalConfig);
285         final DisplayState vrDisplay = reportedDisplays.getDisplayState(VR_VIRTUAL_DISPLAY_WIDTH,
286             VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
287         assertNotNull("Vr mode should have a virtual display", vrDisplay);
288 
289         // Check if the focused activity is on this virtual stack.
290         assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mDisplayId,
291             focusedStack.mDisplayId);
292 
293         // Put the device out of persistent vr mode.
294         enablePersistentVrMode(false);
295 
296         // There isn't a direct launch of activity which can take an user out of persistent VR mode.
297         // This sleep is to account for that delay and let device settle once it comes out of VR
298         // mode.
299         try {
300             Thread.sleep(2000);
301         } catch (Exception e) {
302             e.printStackTrace();
303         }
304 
305         // Launch the non-VR 2D activity and check where it ends up.
306         launchActivity(RESIZEABLE_ACTIVITY_NAME);
307         mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME});
308 
309         // Ensure that the subsequent activity is visible
310         mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
311 
312         // Check that activity is launched in focused stack on primary display.
313         mAmWmState.assertFocusedActivity("Launched activity must be focused", RESIZEABLE_ACTIVITY_NAME);
314         final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
315         final ActivityManagerState.ActivityStack frontStack
316                 = mAmWmState.getAmState().getStackById(frontStackId);
317         assertEquals("Launched activity must be resumed in front stack",
318                 getActivityComponentName(RESIZEABLE_ACTIVITY_NAME), frontStack.mResumedActivity);
319         assertEquals("Front stack must be on primary display",
320                 DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
321     }
322 
testCreateMultipleVirtualDisplays()323     public void testCreateMultipleVirtualDisplays() throws Exception {
324         // Create new virtual display.
325         final List<DisplayState> newDisplays = new VirtualDisplayBuilder(this).build(3);
326         destroyVirtualDisplays();
327         getDisplayStateAfterChange(1);
328     }
329 
330     /**
331      * Tests launching an activity on virtual display.
332      */
333     @Presubmit
testLaunchActivityOnSecondaryDisplay()334     public void testLaunchActivityOnSecondaryDisplay() throws Exception {
335         if (!supportsMultiDisplay()) { return; }
336 
337         // Create new virtual display.
338         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
339 
340         // Launch activity on new secondary display.
341         final String logSeparator = clearLogcat();
342         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
343         mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
344 
345         mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
346                 TEST_ACTIVITY_NAME);
347 
348         // Check that activity is on the right display.
349         final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
350         final ActivityManagerState.ActivityStack frontStack
351                 = mAmWmState.getAmState().getStackById(frontStackId);
352         assertEquals("Launched activity must be on the secondary display and resumed",
353                 getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
354         mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
355 
356         // Check that activity config corresponds to display config.
357         final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY_NAME,
358                 logSeparator);
359         assertEquals("Activity launched on secondary display must have proper configuration",
360                 CUSTOM_DENSITY_DPI, reportedSizes.densityDpi);
361     }
362 
363     /**
364      * Tests launching a non-resizeable activity on virtual display. It should land on the
365      * default display.
366      */
testLaunchNonResizeableActivityOnSecondaryDisplay()367     public void testLaunchNonResizeableActivityOnSecondaryDisplay() throws Exception {
368         if (!supportsMultiDisplay()) { return; }
369 
370         // Create new virtual display.
371         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
372 
373         // Launch activity on new secondary display.
374         launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
375         mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
376 
377         mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
378                 NON_RESIZEABLE_ACTIVITY_NAME);
379 
380         // Check that activity is on the right display.
381         final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
382         final ActivityManagerState.ActivityStack frontStack =
383                 mAmWmState.getAmState().getStackById(frontStackId);
384         assertEquals("Launched activity must be on the primary display and resumed",
385                 getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
386                 frontStack.mResumedActivity);
387         mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
388     }
389 
390     /**
391      * Tests launching a non-resizeable activity on virtual display while split-screen is active
392      * on the primary display. It should land on the primary display and dismiss docked stack.
393      */
testLaunchNonResizeableActivityWithSplitScreen()394     public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception {
395         if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
396 
397         // Start launching activity.
398         launchActivityInDockStack(LAUNCHING_ACTIVITY);
399         // Create new virtual display.
400         final DisplayState newDisplay =
401                 new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
402 
403         // Launch activity on new secondary display.
404         launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
405         mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
406 
407         mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
408                 NON_RESIZEABLE_ACTIVITY_NAME);
409 
410         // Check that activity is on the right display.
411         final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
412         final ActivityManagerState.ActivityStack frontStack =
413                 mAmWmState.getAmState().getStackById(frontStackId);
414         assertEquals("Launched activity must be on the primary display and resumed",
415                 getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
416                 frontStack.mResumedActivity);
417         mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
418         mAmWmState.assertDoesNotContainStack("Must not contain docked stack.", DOCKED_STACK_ID);
419     }
420 
421     /**
422      * Tests moving a non-resizeable activity to a virtual display. It should land on the default
423      * display.
424      */
testMoveNonResizeableActivityToSecondaryDisplay()425     public void testMoveNonResizeableActivityToSecondaryDisplay() throws Exception {
426         if (!supportsMultiDisplay()) { return; }
427 
428         // Create new virtual display.
429         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
430         // Launch a non-resizeable activity on a primary display.
431         launchActivityInNewTask(NON_RESIZEABLE_ACTIVITY_NAME);
432         // Launch a resizeable activity on new secondary display to create a new stack there.
433         launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
434         int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
435 
436         // Try to move the non-resizeable activity to new secondary display.
437         moveActivityToStack(NON_RESIZEABLE_ACTIVITY_NAME, frontStackId);
438         mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
439 
440         mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
441                 NON_RESIZEABLE_ACTIVITY_NAME);
442 
443         // Check that activity is on the right display.
444         frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
445         final ActivityManagerState.ActivityStack frontStack =
446                 mAmWmState.getAmState().getStackById(frontStackId);
447         assertEquals("Launched activity must be on the primary display and resumed",
448                 getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
449                 frontStack.mResumedActivity);
450         mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
451     }
452 
453     /**
454      * Tests launching a non-resizeable activity on virtual display from activity there. It should
455      * land on the secondary display based on the resizeability of the root activity of the task.
456      */
testLaunchNonResizeableActivityFromSecondaryDisplaySameTask()457     public void testLaunchNonResizeableActivityFromSecondaryDisplaySameTask() throws Exception {
458         if (!supportsMultiDisplay()) { return; }
459 
460         // Create new virtual display.
461         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
462 
463         // Launch activity on new secondary display.
464         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
465         mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
466                 LAUNCHING_ACTIVITY);
467 
468         // Check that launching activity is on the secondary display.
469         int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
470         ActivityManagerState.ActivityStack frontStack =
471                 mAmWmState.getAmState().getStackById(frontStackId);
472         assertEquals("Launched activity must be on the secondary display and resumed",
473                 getActivityComponentName(LAUNCHING_ACTIVITY),
474                 frontStack.mResumedActivity);
475         mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
476 
477         // Launch non-resizeable activity from secondary display.
478         getLaunchActivityBuilder().setTargetActivityName(NON_RESIZEABLE_ACTIVITY_NAME).execute();
479 
480         // Check that non-resizeable activity is on the secondary display, because of the resizeable
481         // root of the task.
482         frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
483         frontStack = mAmWmState.getAmState().getStackById(frontStackId);
484         assertEquals("Launched activity must be on the primary display and resumed",
485                 getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
486                 frontStack.mResumedActivity);
487         mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
488     }
489 
490     /**
491      * Tests launching a non-resizeable activity on virtual display from activity there. It should
492      * land on some different suitable display (usually - on the default one).
493      */
testLaunchNonResizeableActivityFromSecondaryDisplayNewTask()494     public void testLaunchNonResizeableActivityFromSecondaryDisplayNewTask() throws Exception {
495         if (!supportsMultiDisplay()) { return; }
496 
497         // Create new virtual display.
498         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
499 
500         // Launch activity on new secondary display.
501         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
502         mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
503                 LAUNCHING_ACTIVITY);
504 
505         // Check that launching activity is on the secondary display.
506         int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
507         ActivityManagerState.ActivityStack frontStack =
508                 mAmWmState.getAmState().getStackById(frontStackId);
509         assertEquals("Launched activity must be on the secondary display and resumed",
510                 getActivityComponentName(LAUNCHING_ACTIVITY),
511                 frontStack.mResumedActivity);
512         mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
513 
514         // Launch non-resizeable activity from secondary display.
515         getLaunchActivityBuilder().setTargetActivityName(NON_RESIZEABLE_ACTIVITY_NAME)
516                 .setNewTask(true).setMultipleTask(true).execute();
517 
518         // Check that non-resizeable activity is on the primary display.
519         frontStackId = mAmWmState.getAmState().getFocusedStackId();
520         frontStack = mAmWmState.getAmState().getStackById(frontStackId);
521         assertFalse("Launched activity must be on a different display",
522                 newDisplay.mDisplayId == frontStack.mDisplayId);
523         assertEquals("Launched activity must be resumed",
524                 getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
525                 frontStack.mResumedActivity);
526         mAmWmState.assertFocusedStack("Focus must be on a just launched activity", frontStackId);
527     }
528 
529     /**
530      * Tests launching a not embedded activity on virtual display. It should land on the
531      * default display.
532      */
testLaunchNotEmbeddedOnVirtualDisplay()533     public void testLaunchNotEmbeddedOnVirtualDisplay() throws Exception {
534         if (!supportsMultiDisplay()) { return; }
535 
536         // Create new virtual display.
537         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
538 
539         final String logSeparator = clearLogcat();
540 
541         // Launch other activity with different uid and check it is launched on primary display.
542         final String broadcastAction = SECOND_PACKAGE_NAME + ".LAUNCH_BROADCAST_ACTION";
543         final String includeStoppedPackagesFlag = " -f 0x00000020";
544         executeShellCommand("am broadcast -a " + broadcastAction + " -p " + SECOND_PACKAGE_NAME
545                 + " --ei target_display " + newDisplay.mDisplayId + includeStoppedPackagesFlag
546                 + " --es package_name " + componentName
547                 + " --es target_activity " + TEST_ACTIVITY_NAME);
548 
549         int tries = 0;
550         boolean match = false;
551         final Pattern pattern = Pattern.compile(".*SecurityException launching activity.*");
552         while (tries < 5 && !match) {
553             String[] logs = getDeviceLogsForComponent("LaunchBroadcastReceiver", logSeparator);
554             for (String line : logs) {
555                 Matcher m = pattern.matcher(line);
556                 if (m.matches()) {
557                     match = true;
558                     break;
559                 }
560             }
561             tries++;
562             try {
563                 Thread.sleep(500);
564             } catch (InterruptedException e) {
565             }
566         }
567 
568         assertTrue("Expected exception not found", match);
569 
570         mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
571         assertFalse("Restricted activity must not be launched",
572                 mAmWmState.getAmState().containsActivity(TEST_ACTIVITY_NAME));
573     }
574 
575     /**
576      * Tests launching an activity on virtual display and then launching another activity via shell
577      * command and without specifying the display id - the second activity must appear on the
578      * primary display.
579      */
580     @Presubmit
testConsequentLaunchActivity()581     public void testConsequentLaunchActivity() throws Exception {
582         if (!supportsMultiDisplay()) { return; }
583 
584         // Create new virtual display.
585         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
586 
587         // Launch activity on new secondary display.
588         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
589         mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
590 
591         mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
592                 TEST_ACTIVITY_NAME);
593 
594         // Launch second activity without specifying display.
595         launchActivity(LAUNCHING_ACTIVITY);
596         mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
597 
598         // Check that activity is launched in focused stack on primary display.
599         mAmWmState.assertFocusedActivity("Launched activity must be focused", LAUNCHING_ACTIVITY);
600         final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
601         final ActivityManagerState.ActivityStack frontStack
602                 = mAmWmState.getAmState().getStackById(frontStackId);
603         assertEquals("Launched activity must be resumed in front stack",
604                 getActivityComponentName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity);
605         assertEquals("Front stack must be on primary display",
606                 DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
607     }
608 
609     /**
610      * Tests launching an activity on virtual display and then launching another activity from the
611      * first one - it must appear on the secondary display, because it was launched from there.
612      */
613     @Presubmit
testConsequentLaunchActivityFromSecondaryDisplay()614     public void testConsequentLaunchActivityFromSecondaryDisplay() throws Exception {
615         if (!supportsMultiDisplay()) { return; }
616 
617         // Create new virtual display.
618         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
619 
620         // Launch activity on new secondary display.
621         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
622         mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
623 
624         mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
625                 LAUNCHING_ACTIVITY);
626 
627         // Launch second activity from app on secondary display without specifying display id.
628         getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
629         mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
630 
631         // Check that activity is launched in focused stack on external display.
632         mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
633         final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
634         final ActivityManagerState.ActivityStack frontStack
635                 = mAmWmState.getAmState().getStackById(frontStackId);
636         assertEquals("Launched activity must be resumed in front stack",
637                 getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
638     }
639 
640     /**
641      * Tests launching activities on secondary and then on primary display to see if the stack
642      * visibility is not affected.
643      */
644     @Presubmit
testLaunchActivitiesAffectsVisibility()645     public void testLaunchActivitiesAffectsVisibility() throws Exception {
646         if (!supportsMultiDisplay()) { return; }
647 
648         // Start launching activity.
649         launchActivity(LAUNCHING_ACTIVITY);
650         // Create new virtual display.
651         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
652         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
653 
654         // Launch activity on new secondary display.
655         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
656         mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
657         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
658 
659         // Launch activity on primary display and check if it doesn't affect activity on secondary
660         // display.
661         getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
662         mAmWmState.waitForValidState(mDevice, RESIZEABLE_ACTIVITY_NAME);
663         mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
664         mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
665     }
666 
667     /**
668      * Test that move-task works when moving between displays.
669      */
670     @Presubmit
testMoveTaskBetweenDisplays()671     public void testMoveTaskBetweenDisplays() throws Exception {
672         if (!supportsMultiDisplay()) { return; }
673 
674         // Create new virtual display.
675         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
676         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
677         mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
678                 VIRTUAL_DISPLAY_ACTIVITY);
679         final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
680         ActivityManagerState.ActivityStack focusedStack
681                 = mAmWmState.getAmState().getStackById(defaultDisplayStackId);
682         assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
683                 focusedStack.mDisplayId);
684 
685         // Launch activity on new secondary display.
686         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
687         mAmWmState.assertFocusedActivity("Focus must be on secondary display", TEST_ACTIVITY_NAME);
688         int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
689         focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
690         assertEquals("Focused stack must be on secondary display",
691                 newDisplay.mDisplayId, focusedStack.mDisplayId);
692 
693         // Move activity from secondary display to primary.
694         moveActivityToStack(TEST_ACTIVITY_NAME, defaultDisplayStackId);
695         mAmWmState.waitForFocusedStack(mDevice, defaultDisplayStackId);
696         mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY_NAME);
697         focusedStackId = mAmWmState.getAmState().getFocusedStackId();
698         focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
699         assertEquals("Focus must return to primary display", DEFAULT_DISPLAY_ID,
700                 focusedStack.mDisplayId);
701     }
702 
703     /**
704      * Tests launching activities on secondary display and then removing it to see if stack focus
705      * is moved correctly.
706      * This version launches virtual display creator to fullscreen stack in split-screen.
707      */
708     @Presubmit
testStackFocusSwitchOnDisplayRemoved()709     public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
710         if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
711 
712         // Start launching activity into docked stack.
713         launchActivityInDockStack(LAUNCHING_ACTIVITY);
714         mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
715 
716         tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
717                 FULLSCREEN_WORKSPACE_STACK_ID);
718     }
719 
720     /**
721      * Tests launching activities on secondary display and then removing it to see if stack focus
722      * is moved correctly.
723      * This version launches virtual display creator to docked stack in split-screen.
724      */
testStackFocusSwitchOnDisplayRemoved2()725     public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
726         if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
727 
728         // Setup split-screen.
729         launchActivityInDockStack(RESIZEABLE_ACTIVITY_NAME);
730 
731         // Start launching activity into fullscreen stack.
732         launchActivityInStack(LAUNCHING_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
733         mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
734 
735         tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
736                 FULLSCREEN_WORKSPACE_STACK_ID);
737     }
738 
739     /**
740      * Tests launching activities on secondary display and then removing it to see if stack focus
741      * is moved correctly.
742      * This version works without split-screen.
743      */
testStackFocusSwitchOnDisplayRemoved3()744     public void testStackFocusSwitchOnDisplayRemoved3() throws Exception {
745         if (!supportsMultiDisplay()) { return; }
746 
747         // Start an activity on default display to determine default stack.
748         launchActivity(BROADCAST_RECEIVER_ACTIVITY);
749         final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
750         // Finish probing activity.
751         executeShellCommand(FINISH_ACTIVITY_BROADCAST);
752 
753 
754         tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */, focusedStackId);
755     }
756 
757     /**
758      * Create a virtual display, launch a test activity there, destroy the display and check if test
759      * activity is moved to a stack on the default display.
760      */
tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int defaultStackId)761     private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int defaultStackId)
762             throws Exception {
763         // Create new virtual display.
764         final VirtualDisplayBuilder builder = new VirtualDisplayBuilder(this)
765                 .setPublicDisplay(true);
766         if (splitScreen) {
767             builder.setLaunchInSplitScreen(true);
768         }
769         final DisplayState newDisplay = builder.build();
770         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
771         if (splitScreen) {
772             mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
773         }
774 
775         // Launch activity on new secondary display.
776         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
777         mAmWmState.assertFocusedActivity("Focus must be on secondary display",
778                 TEST_ACTIVITY_NAME);
779         final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
780         mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
781 
782         // Destroy virtual display.
783         destroyVirtualDisplays();
784         mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY_NAME, defaultStackId);
785         mAmWmState.assertSanity();
786         mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */);
787 
788         // Check if the focus is switched back to primary display.
789         mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
790         mAmWmState.assertFocusedStack(
791                 "Default stack on primary display must be focused after display removed",
792                 defaultStackId);
793         mAmWmState.assertFocusedActivity(
794                 "Focus must be switched back to activity on primary display",
795                 TEST_ACTIVITY_NAME);
796     }
797 
798     /**
799      * Tests launching activities on secondary display and then removing it to see if stack focus
800      * is moved correctly.
801      */
testStackFocusSwitchOnStackEmptied()802     public void testStackFocusSwitchOnStackEmptied() throws Exception {
803         if (!supportsMultiDisplay()) { return; }
804 
805         // Create new virtual display.
806         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
807         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
808         final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
809 
810         // Launch activity on new secondary display.
811         launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mDisplayId);
812         mAmWmState.assertFocusedActivity("Focus must be on secondary display",
813                 BROADCAST_RECEIVER_ACTIVITY);
814 
815         // Lock the device, so that activity containers will be detached.
816         sleepDevice();
817 
818         // Finish activity on secondary display.
819         executeShellCommand(FINISH_ACTIVITY_BROADCAST);
820 
821         // Unlock and check if the focus is switched back to primary display.
822         wakeUpAndUnlockDevice();
823         mAmWmState.waitForFocusedStack(mDevice, focusedStackId);
824         mAmWmState.waitForValidState(mDevice, VIRTUAL_DISPLAY_ACTIVITY);
825         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
826         mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
827                 VIRTUAL_DISPLAY_ACTIVITY);
828     }
829 
830     /**
831      * Tests that input events on the primary display take focus from the virtual display.
832      */
testStackFocusSwitchOnTouchEvent()833     public void testStackFocusSwitchOnTouchEvent() throws Exception {
834         if (!supportsMultiDisplay()) { return; }
835 
836         // Create new virtual display.
837         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
838 
839         mAmWmState.computeState(mDevice, new String[] {VIRTUAL_DISPLAY_ACTIVITY});
840         mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
841                 VIRTUAL_DISPLAY_ACTIVITY);
842 
843         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
844 
845         mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
846         mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
847                 TEST_ACTIVITY_NAME);
848 
849         final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
850         final int width = displayMetrics.getWidth();
851         final int height = displayMetrics.getHeight();
852         executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
853 
854         mAmWmState.computeState(mDevice, new String[] {VIRTUAL_DISPLAY_ACTIVITY});
855         mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
856                 VIRTUAL_DISPLAY_ACTIVITY);
857     }
858 
859     /** Test that system is allowed to launch on secondary displays. */
testPermissionLaunchFromSystem()860     public void testPermissionLaunchFromSystem() throws Exception {
861         if (!supportsMultiDisplay()) { return; }
862 
863         // Create new virtual display.
864         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
865         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
866         mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
867                 VIRTUAL_DISPLAY_ACTIVITY);
868         final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
869         ActivityManagerState.ActivityStack focusedStack
870                 = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
871         assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
872                 focusedStack.mDisplayId);
873 
874         // Launch activity on new secondary display.
875         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
876         mAmWmState.assertFocusedActivity("Focus must be on secondary display",
877                 TEST_ACTIVITY_NAME);
878         final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
879         focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
880         assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
881                 focusedStack.mDisplayId);
882 
883         // Launch other activity with different uid and check it is launched on dynamic stack on
884         // secondary display.
885         final String startCmd =  "am start -n " + SECOND_PACKAGE_NAME + "/." + SECOND_ACTIVITY_NAME
886                 + " --display " + newDisplay.mDisplayId;
887         executeShellCommand(startCmd);
888 
889         mAmWmState.waitForValidState(mDevice, new String[] {SECOND_ACTIVITY_NAME},
890                 null /* stackIds */, false /* compareTaskAndStackBounds */, SECOND_PACKAGE_NAME);
891         mAmWmState.assertFocusedActivity("Focus must be on newly launched app", SECOND_PACKAGE_NAME,
892                 SECOND_ACTIVITY_NAME);
893         assertEquals("Activity launched by system must be on external display",
894                 externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
895     }
896 
897     /** Tests that an activity can launch an activity from a different UID into its own task. */
testPermissionLaunchMultiUidTask()898     public void testPermissionLaunchMultiUidTask() throws Exception {
899         if (!supportsMultiDisplay()) { return; }
900 
901         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
902 
903         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
904         mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
905 
906         // Check that the first activity is launched onto the secondary display
907         final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
908         ActivityManagerState.ActivityStack frontStack =
909                 mAmWmState.getAmState().getStackById(frontStackId);
910         assertEquals("Activity launched on secondary display must be resumed",
911                 getActivityComponentName(LAUNCHING_ACTIVITY),
912                 frontStack.mResumedActivity);
913         mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
914 
915         // Launch an activity from a different UID into the first activity's task
916         getLaunchActivityBuilder()
917                 .setTargetPackage(SECOND_PACKAGE_NAME)
918                 .setTargetActivityName(SECOND_ACTIVITY_NAME).execute();
919 
920         mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
921         frontStack = mAmWmState.getAmState().getStackById(frontStackId);
922         mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
923                 SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME);
924         assertEquals("Secondary display must contain 1 task", 1, frontStack.getTasks().size());
925     }
926 
927     /**
928      * Test that launching from app that is not present on external display and doesn't own it to
929      * that external display is not allowed.
930      */
testPermissionLaunchFromDifferentApp()931     public void testPermissionLaunchFromDifferentApp() throws Exception {
932         if (!supportsMultiDisplay()) { return; }
933 
934         // Create new virtual display.
935         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
936         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
937         mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
938                 VIRTUAL_DISPLAY_ACTIVITY);
939         final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
940         ActivityManagerState.ActivityStack focusedStack
941                 = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
942         assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
943                 focusedStack.mDisplayId);
944 
945         // Launch activity on new secondary display.
946         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
947         mAmWmState.assertFocusedActivity("Focus must be on secondary display",
948                 TEST_ACTIVITY_NAME);
949         final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
950         focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
951         assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
952                 focusedStack.mDisplayId);
953 
954         final String logSeparator = clearLogcat();
955 
956         // Launch other activity with different uid and check it is launched on primary display.
957         final String broadcastAction = SECOND_PACKAGE_NAME + ".LAUNCH_BROADCAST_ACTION";
958         final String includeStoppedPackagesFlag = " -f 0x00000020";
959         executeShellCommand("am broadcast -a " + broadcastAction + " -p " + SECOND_PACKAGE_NAME
960                 + " --ei target_display " + newDisplay.mDisplayId + includeStoppedPackagesFlag);
961 
962         int tries = 0;
963         boolean match = false;
964         final Pattern pattern = Pattern.compile(".*SecurityException launching activity.*");
965         while (tries < 5 && !match) {
966             String[] logs = getDeviceLogsForComponent("LaunchBroadcastReceiver", logSeparator);
967             for (String line : logs) {
968                 Matcher m = pattern.matcher(line);
969                 if (m.matches()) {
970                     match = true;
971                     break;
972                 }
973             }
974             tries++;
975             try {
976                 Thread.sleep(500);
977             } catch (InterruptedException e) {
978             }
979         }
980 
981         assertTrue("Expected exception not found", match);
982 
983         mAmWmState.waitForValidState(mDevice, new String[] {TEST_ACTIVITY_NAME},
984                 null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
985         mAmWmState.assertFocusedActivity(
986                 "Focus must be on first activity", componentName, TEST_ACTIVITY_NAME);
987         assertEquals("Focused stack must be on secondary display's stack",
988                 externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
989     }
990 
991     /**
992      * Test that all activities that were on the private display are destroyed on display removal.
993      */
994     @Presubmit
testContentDestroyOnDisplayRemoved()995     public void testContentDestroyOnDisplayRemoved() throws Exception {
996         if (!supportsMultiDisplay()) { return; }
997 
998         // Create new private virtual display.
999         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
1000         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
1001 
1002         // Launch activities on new secondary display.
1003         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
1004         mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
1005         mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
1006         launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
1007         mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
1008         mAmWmState.assertFocusedActivity("Launched activity must be focused",
1009                 RESIZEABLE_ACTIVITY_NAME);
1010 
1011         // Destroy the display and check if activities are removed from system.
1012         final String logSeparator = clearLogcat();
1013         destroyVirtualDisplays();
1014         final String activityName1
1015                 = ActivityManagerTestBase.getActivityComponentName(TEST_ACTIVITY_NAME);
1016         final String activityName2
1017                 = ActivityManagerTestBase.getActivityComponentName(RESIZEABLE_ACTIVITY_NAME);
1018         final String windowName1
1019                 = ActivityManagerTestBase.getWindowName(TEST_ACTIVITY_NAME);
1020         final String windowName2
1021                 = ActivityManagerTestBase.getWindowName(RESIZEABLE_ACTIVITY_NAME);
1022         mAmWmState.waitForWithAmState(mDevice,
1023                 (state) -> !state.containsActivity(activityName1)
1024                         && !state.containsActivity(activityName2),
1025                 "Waiting for activity to be removed");
1026         mAmWmState.waitForWithWmState(mDevice,
1027                 (state) -> !state.containsWindow(windowName1)
1028                         && !state.containsWindow(windowName2),
1029                 "Waiting for activity window to be gone");
1030 
1031         // Check AM state.
1032         assertFalse("Activity from removed display must be destroyed",
1033                 mAmWmState.getAmState().containsActivity(activityName1));
1034         assertFalse("Activity from removed display must be destroyed",
1035                 mAmWmState.getAmState().containsActivity(activityName2));
1036         // Check WM state.
1037         assertFalse("Activity windows from removed display must be destroyed",
1038                 mAmWmState.getWmState().containsWindow(windowName1));
1039         assertFalse("Activity windows from removed display must be destroyed",
1040                 mAmWmState.getWmState().containsWindow(windowName2));
1041         // Check activity logs.
1042         assertActivityDestroyed(TEST_ACTIVITY_NAME, logSeparator);
1043         assertActivityDestroyed(RESIZEABLE_ACTIVITY_NAME, logSeparator);
1044     }
1045 
1046     /**
1047      * Test that the update of display metrics updates all its content.
1048      */
1049     @Presubmit
testDisplayResize()1050     public void testDisplayResize() throws Exception {
1051         if (!supportsMultiDisplay()) { return; }
1052 
1053         // Create new virtual display.
1054         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
1055         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
1056 
1057         // Launch a resizeable activity on new secondary display.
1058         final String initialLogSeparator = clearLogcat();
1059         launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
1060         mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
1061         mAmWmState.assertFocusedActivity("Launched activity must be focused",
1062                 RESIZEABLE_ACTIVITY_NAME);
1063 
1064         // Grab reported sizes and compute new with slight size change.
1065         final ReportedSizes initialSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
1066                 initialLogSeparator);
1067 
1068         // Resize the docked stack, so that activity with virtual display will also be resized.
1069         final String logSeparator = clearLogcat();
1070         executeShellCommand(getResizeVirtualDisplayCommand());
1071 
1072         mAmWmState.waitForWithAmState(mDevice, amState -> {
1073             try {
1074                 return readConfigChangeNumber(RESIZEABLE_ACTIVITY_NAME, logSeparator) == 1
1075                         && amState.hasActivityState(RESIZEABLE_ACTIVITY_NAME, STATE_RESUMED);
1076             } catch (Exception e) {
1077                 logE("Error waiting for valid state: " + e.getMessage());
1078                 return false;
1079             }
1080         }, "Wait for the configuration change to happen and for activity to be resumed.");
1081 
1082         mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME,
1083                 VIRTUAL_DISPLAY_ACTIVITY}, false /* compareTaskAndStackBounds */);
1084         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
1085         mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true);
1086 
1087         // Check if activity in virtual display was resized properly.
1088         assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY_NAME, 0 /* numRelaunch */,
1089                 1 /* numConfigChange */, logSeparator);
1090 
1091         final ReportedSizes updatedSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
1092                 logSeparator);
1093         assertTrue(updatedSize.widthDp <= initialSize.widthDp);
1094         assertTrue(updatedSize.heightDp <= initialSize.heightDp);
1095         assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2);
1096         assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2);
1097     }
1098 
1099     /** Read the number of configuration changes sent to activity from logs. */
readConfigChangeNumber(String activityName, String logSeparator)1100     private int readConfigChangeNumber(String activityName, String logSeparator) throws Exception {
1101         return (new ActivityLifecycleCounts(activityName, logSeparator)).mConfigurationChangedCount;
1102     }
1103 
1104     /**
1105      * Tests that when an activity is launched with displayId specified and there is an existing
1106      * matching task on some other display - that task will moved to the target display.
1107      */
testMoveToDisplayOnLaunch()1108     public void testMoveToDisplayOnLaunch() throws Exception {
1109         if (!supportsMultiDisplay()) { return; }
1110 
1111         // Launch activity with unique affinity, so it will the only one in its task.
1112         launchActivity(LAUNCHING_ACTIVITY);
1113 
1114         // Create new virtual display.
1115         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
1116         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
1117         final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
1118         // Launch something to that display so that a new stack is created. We need this to be able
1119         // to compare task numbers in stacks later.
1120         launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
1121         mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
1122 
1123         final int taskNum = mAmWmState.getAmState().getStackById(defaultDisplayStackId)
1124                 .getTasks().size();
1125         final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
1126         final int taskNumOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
1127                 .getTasks().size();
1128 
1129         // Launch activity on new secondary display.
1130         // Using custom command here, because normally we add flags Intent#FLAG_ACTIVITY_NEW_TASK
1131         // and Intent#FLAG_ACTIVITY_MULTIPLE_TASK when launching on some specific display. We don't
1132         // do it here as we want an existing task to be used.
1133         final String launchCommand = "am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY)
1134                 + " --display " + newDisplay.mDisplayId;
1135         executeShellCommand(launchCommand);
1136         mAmWmState.waitForActivityState(mDevice, LAUNCHING_ACTIVITY, STATE_RESUMED);
1137 
1138         // Check that activity is brought to front.
1139         mAmWmState.assertFocusedActivity("Existing task must be brought to front",
1140                 LAUNCHING_ACTIVITY);
1141         mAmWmState.assertResumedActivity("Existing task must be resumed", LAUNCHING_ACTIVITY);
1142 
1143         // Check that activity is on the right display.
1144         final ActivityManagerState.ActivityStack firstFrontStack =
1145                 mAmWmState.getAmState().getStackById(frontStackId);
1146         assertEquals("Activity must be moved to the secondary display",
1147                 getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
1148         mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
1149 
1150         // Check that task has moved from primary display to secondary.
1151         final int taskNumFinal = mAmWmState.getAmState().getStackById(defaultDisplayStackId)
1152                 .getTasks().size();
1153         mAmWmState.assertEquals("Task number in default stack must be decremented.", taskNum - 1,
1154                 taskNumFinal);
1155         final int taskNumFinalOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
1156                 .getTasks().size();
1157         mAmWmState.assertEquals("Task number in stack on external display must be incremented.",
1158                 taskNumOnSecondary + 1, taskNumFinalOnSecondary);
1159     }
1160 
1161     /**
1162      * Tests that when primary display is rotated secondary displays are not affected.
1163      */
testRotationNotAffectingSecondaryScreen()1164     public void testRotationNotAffectingSecondaryScreen() throws Exception {
1165         if (!supportsMultiDisplay()) { return; }
1166 
1167         // Create new virtual display.
1168         final DisplayState newDisplay = new VirtualDisplayBuilder(this)
1169                 .setResizeDisplay(false)
1170                 .build();
1171         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
1172 
1173         // Launch activity on new secondary display.
1174         String logSeparator = clearLogcat();
1175         launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
1176         mAmWmState.assertFocusedActivity("Focus must be on secondary display",
1177                 RESIZEABLE_ACTIVITY_NAME);
1178         final ReportedSizes initialSizes = getLastReportedSizesForActivity(
1179                 RESIZEABLE_ACTIVITY_NAME, logSeparator);
1180         assertNotNull("Test activity must have reported initial sizes on launch", initialSizes);
1181 
1182         // Rotate primary display and check that activity on secondary display is not affected.
1183         rotateAndCheckSameSizes(RESIZEABLE_ACTIVITY_NAME);
1184 
1185         // Launch activity to secondary display when primary one is rotated.
1186         final int initialRotation = mAmWmState.getWmState().getRotation();
1187         setDeviceRotation((initialRotation + 1) % 4);
1188 
1189         logSeparator = clearLogcat();
1190         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
1191         mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY_NAME, STATE_RESUMED);
1192         mAmWmState.assertFocusedActivity("Focus must be on secondary display",
1193                 TEST_ACTIVITY_NAME);
1194         final ReportedSizes testActivitySizes = getLastReportedSizesForActivity(
1195                 TEST_ACTIVITY_NAME, logSeparator);
1196         assertEquals("Sizes of secondary display must not change after rotation of primary display",
1197                 initialSizes, testActivitySizes);
1198     }
1199 
rotateAndCheckSameSizes(String activityName)1200     private void rotateAndCheckSameSizes(String activityName) throws Exception {
1201         for (int rotation = 3; rotation >= 0; --rotation) {
1202             final String logSeparator = clearLogcat();
1203             setDeviceRotation(rotation);
1204             final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName,
1205                     logSeparator);
1206             assertNull("Sizes must not change after rotation", rotatedSizes);
1207         }
1208     }
1209 
1210     /**
1211      * Tests that task affinity does affect what display an activity is launched on but that
1212      * matching the task component root does.
1213      */
testTaskMatchAcrossDisplays()1214     public void testTaskMatchAcrossDisplays() throws Exception {
1215         if (!supportsMultiDisplay()) { return; }
1216 
1217         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
1218 
1219         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
1220         mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
1221 
1222         // Check that activity is on the right display.
1223         final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
1224         final ActivityManagerState.ActivityStack firstFrontStack =
1225                 mAmWmState.getAmState().getStackById(frontStackId);
1226         assertEquals("Activity launched on secondary display must be resumed",
1227                 getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
1228         mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
1229 
1230         executeShellCommand("am start -n " + getActivityComponentName(ALT_LAUNCHING_ACTIVITY));
1231         mAmWmState.waitForValidState(mDevice, new String[] {ALT_LAUNCHING_ACTIVITY},
1232                 null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
1233 
1234         // Check that second activity gets launched on the default display
1235         final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId(
1236                 DEFAULT_DISPLAY_ID);
1237         final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
1238                 mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId);
1239         assertEquals("Activity launched on default display must be resumed",
1240                 getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
1241                 defaultDisplayFrontStack.mResumedActivity);
1242         mAmWmState.assertFocusedStack("Focus must be on primary display",
1243                 defaultDisplayFrontStackId);
1244 
1245         executeShellCommand("am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY));
1246         mAmWmState.waitForFocusedStack(mDevice, frontStackId);
1247 
1248         // Check that the third intent is redirected to the first task
1249         final ActivityManagerState.ActivityStack secondFrontStack
1250                 = mAmWmState.getAmState().getStackById(frontStackId);
1251         assertEquals("Activity launched on default display must be resumed",
1252                 getActivityComponentName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity);
1253         mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
1254         assertEquals("Focused stack must only contain 1 task",
1255                 1, secondFrontStack.getTasks().size());
1256         assertEquals("Focused task must only contain 1 activity",
1257                 1, secondFrontStack.getTasks().get(0).mActivities.size());
1258     }
1259 
1260     /**
1261      * Tests than a new task launched by an activity will end up on that activity's display
1262      * even if the focused stack is not on that activity's display.
1263      */
testNewTaskSameDisplay()1264     public void testNewTaskSameDisplay() throws Exception {
1265         if (!supportsMultiDisplay()) { return; }
1266 
1267         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
1268 
1269         launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mDisplayId);
1270         mAmWmState.computeState(mDevice, new String[] {BROADCAST_RECEIVER_ACTIVITY});
1271 
1272         // Check that the first activity is launched onto the secondary display
1273         final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
1274         final ActivityManagerState.ActivityStack firstFrontStack =
1275                 mAmWmState.getAmState().getStackById(frontStackId);
1276         assertEquals("Activity launched on secondary display must be resumed",
1277                 getActivityComponentName(BROADCAST_RECEIVER_ACTIVITY),
1278                 firstFrontStack.mResumedActivity);
1279         mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
1280 
1281         executeShellCommand("am start -n " + getActivityComponentName(TEST_ACTIVITY_NAME));
1282         mAmWmState.waitForValidState(mDevice, new String[] {TEST_ACTIVITY_NAME},
1283                 null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
1284 
1285         // Check that the second activity is launched on the default display
1286         final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
1287         final ActivityManagerState.ActivityStack focusedStack
1288                 = mAmWmState.getAmState().getStackById(focusedStackId);
1289         assertEquals("Activity launched on default display must be resumed",
1290                 getActivityComponentName(TEST_ACTIVITY_NAME), focusedStack.mResumedActivity);
1291         assertEquals("Focus must be on primary display", DEFAULT_DISPLAY_ID,
1292                 focusedStack.mDisplayId);
1293 
1294         executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
1295                 + "--ez new_task true --es target_activity " + LAUNCHING_ACTIVITY);
1296 
1297         // Check that the third activity ends up in a new task in the same stack as the
1298         // first activity
1299         mAmWmState.waitForValidState(mDevice, new String[] {LAUNCHING_ACTIVITY},
1300                 null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
1301         mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
1302         final ActivityManagerState.ActivityStack secondFrontStack =
1303                 mAmWmState.getAmState().getStackById(frontStackId);
1304         assertEquals("Activity must be launched on secondary display",
1305                 getActivityComponentName(LAUNCHING_ACTIVITY),
1306                 secondFrontStack.mResumedActivity);
1307         assertEquals("Secondary display must contain 2 tasks",
1308                 2, secondFrontStack.getTasks().size());
1309     }
1310 
1311     /**
1312      * Test that display overrides apply correctly and won't be affected by display changes.
1313      * This sets overrides to display size and density, initiates a display changed event by locking
1314      * and unlocking the phone and verifies that overrides are kept.
1315      */
1316     @Presubmit
testForceDisplayMetrics()1317     public void testForceDisplayMetrics() throws Exception {
1318         launchHomeActivity();
1319 
1320         // Read initial sizes.
1321         final ReportedDisplayMetrics originalDisplayMetrics = getDisplayMetrics();
1322 
1323         // Apply new override values that don't match the physical metrics.
1324         final int overrideWidth = (int) (originalDisplayMetrics.physicalWidth * 1.5);
1325         final int overrideHeight = (int) (originalDisplayMetrics.physicalHeight * 1.5);
1326         executeShellCommand(WM_SIZE + " " + overrideWidth + "x" + overrideHeight);
1327         final int overrideDensity = (int) (originalDisplayMetrics.physicalDensity * 1.1);
1328         executeShellCommand(WM_DENSITY + " " + overrideDensity);
1329 
1330         // Check if overrides applied correctly.
1331         ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
1332         assertEquals(overrideWidth, displayMetrics.overrideWidth);
1333         assertEquals(overrideHeight, displayMetrics.overrideHeight);
1334         assertEquals(overrideDensity, displayMetrics.overrideDensity);
1335 
1336         // Lock and unlock device. This will cause a DISPLAY_CHANGED event to be triggered and
1337         // might update the metrics.
1338         sleepDevice();
1339         wakeUpAndUnlockDevice();
1340         mAmWmState.waitForHomeActivityVisible(mDevice);
1341 
1342         // Check if overrides are still applied.
1343         displayMetrics = getDisplayMetrics();
1344         assertEquals(overrideWidth, displayMetrics.overrideWidth);
1345         assertEquals(overrideHeight, displayMetrics.overrideHeight);
1346         assertEquals(overrideDensity, displayMetrics.overrideDensity);
1347 
1348         // All overrides will be cleared in tearDown.
1349     }
1350 
1351     /** Get physical and override display metrics from WM. */
getDisplayMetrics()1352     private ReportedDisplayMetrics getDisplayMetrics() throws Exception {
1353         mDumpLines.clear();
1354         final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
1355         mDevice.executeShellCommand(WM_SIZE, outputReceiver);
1356         mDevice.executeShellCommand(WM_DENSITY, outputReceiver);
1357         final String dump = outputReceiver.getOutput();
1358         mDumpLines.clear();
1359         Collections.addAll(mDumpLines, dump.split("\\n"));
1360         return ReportedDisplayMetrics.create(mDumpLines);
1361     }
1362 
1363     private static class ReportedDisplayMetrics {
1364         private static final Pattern sPhysicalSizePattern =
1365                 Pattern.compile("Physical size: (\\d+)x(\\d+)");
1366         private static final Pattern sOverrideSizePattern =
1367                 Pattern.compile("Override size: (\\d+)x(\\d+)");
1368         private static final Pattern sPhysicalDensityPattern =
1369                 Pattern.compile("Physical density: (\\d+)");
1370         private static final Pattern sOverrideDensityPattern =
1371                 Pattern.compile("Override density: (\\d+)");
1372 
1373         int physicalWidth;
1374         int physicalHeight;
1375         int physicalDensity;
1376 
1377         boolean sizeOverrideSet;
1378         int overrideWidth;
1379         int overrideHeight;
1380         boolean densityOverrideSet;
1381         int overrideDensity;
1382 
1383         /** Get width that WM operates with. */
getWidth()1384         int getWidth() {
1385             return sizeOverrideSet ? overrideWidth : physicalWidth;
1386         }
1387 
1388         /** Get height that WM operates with. */
getHeight()1389         int getHeight() {
1390             return sizeOverrideSet ? overrideHeight : physicalHeight;
1391         }
1392 
1393         /** Get density that WM operates with. */
getDensity()1394         int getDensity() {
1395             return densityOverrideSet ? overrideDensity : physicalDensity;
1396         }
1397 
create(LinkedList<String> dump)1398         static ReportedDisplayMetrics create(LinkedList<String> dump) {
1399             final ReportedDisplayMetrics result = new ReportedDisplayMetrics();
1400 
1401             boolean physicalSizeFound = false;
1402             boolean physicalDensityFound = false;
1403 
1404             while (!dump.isEmpty()) {
1405                 final String line = dump.pop().trim();
1406 
1407                 Matcher matcher = sPhysicalSizePattern.matcher(line);
1408                 if (matcher.matches()) {
1409                     physicalSizeFound = true;
1410                     log(line);
1411                     result.physicalWidth = Integer.parseInt(matcher.group(1));
1412                     result.physicalHeight = Integer.parseInt(matcher.group(2));
1413                     continue;
1414                 }
1415 
1416                 matcher = sOverrideSizePattern.matcher(line);
1417                 if (matcher.matches()) {
1418                     log(line);
1419                     result.overrideWidth = Integer.parseInt(matcher.group(1));
1420                     result.overrideHeight = Integer.parseInt(matcher.group(2));
1421                     result.sizeOverrideSet = true;
1422                     continue;
1423                 }
1424 
1425                 matcher = sPhysicalDensityPattern.matcher(line);
1426                 if (matcher.matches()) {
1427                     physicalDensityFound = true;
1428                     log(line);
1429                     result.physicalDensity = Integer.parseInt(matcher.group(1));
1430                     continue;
1431                 }
1432 
1433                 matcher = sOverrideDensityPattern.matcher(line);
1434                 if (matcher.matches()) {
1435                     log(line);
1436                     result.overrideDensity = Integer.parseInt(matcher.group(1));
1437                     result.densityOverrideSet = true;
1438                     continue;
1439                 }
1440             }
1441 
1442             assertTrue("Physical display size must be reported", physicalSizeFound);
1443             assertTrue("Physical display density must be reported", physicalDensityFound);
1444 
1445             return result;
1446         }
1447     }
1448 
1449     /** Assert that component received onMovedToDisplay and onConfigurationChanged callbacks. */
assertMovedToDisplay(String componentName, String logSeparator)1450     private void assertMovedToDisplay(String componentName, String logSeparator) throws Exception {
1451         final ActivityLifecycleCounts lifecycleCounts
1452                 = new ActivityLifecycleCounts(componentName, logSeparator);
1453         if (lifecycleCounts.mDestroyCount != 0) {
1454             fail(componentName + " has been destroyed " + lifecycleCounts.mDestroyCount
1455                     + " time(s), wasn't expecting any");
1456         } else if (lifecycleCounts.mCreateCount != 0) {
1457             fail(componentName + " has been (re)created " + lifecycleCounts.mCreateCount
1458                     + " time(s), wasn't expecting any");
1459         } else if (lifecycleCounts.mConfigurationChangedCount != 1) {
1460             fail(componentName + " has received "
1461                     + lifecycleCounts.mConfigurationChangedCount
1462                     + " onConfigurationChanged() calls, expecting " + 1);
1463         } else if (lifecycleCounts.mMovedToDisplayCount != 1) {
1464             fail(componentName + " has received "
1465                     + lifecycleCounts.mMovedToDisplayCount
1466                     + " onMovedToDisplay() calls, expecting " + 1);
1467         }
1468     }
1469 
getResizeVirtualDisplayCommand()1470     private static String getResizeVirtualDisplayCommand() {
1471         return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
1472                 " --es command resize_display";
1473     }
1474 }
1475