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