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