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