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