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 assumeTrue(supportsLockScreen()); 548 549 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession(); 550 final LockScreenSession lockScreenSession = new LockScreenSession()) { 551 validateStackFocusSwitchOnStackEmptied(virtualDisplaySession, lockScreenSession); 552 } 553 } 554 555 /** 556 * Tests launching activities on secondary display and then finishing it to see if stack focus 557 * is moved correctly. 558 */ 559 @Test testStackFocusSwitchOnStackEmptied()560 public void testStackFocusSwitchOnStackEmptied() throws Exception { 561 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 562 validateStackFocusSwitchOnStackEmptied(virtualDisplaySession, 563 null /* lockScreenSession */); 564 } 565 } 566 validateStackFocusSwitchOnStackEmptied(VirtualDisplaySession virtualDisplaySession, LockScreenSession lockScreenSession)567 private void validateStackFocusSwitchOnStackEmptied(VirtualDisplaySession virtualDisplaySession, 568 LockScreenSession lockScreenSession) throws Exception { 569 // Create new virtual display. 570 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 571 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 572 final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY); 573 574 // Launch activity on new secondary display. 575 launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId); 576 waitAndAssertTopResumedActivity(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId, 577 "Top activity must be on secondary display"); 578 579 if (lockScreenSession != null) { 580 // Lock the device, so that activity containers will be detached. 581 lockScreenSession.sleepDevice(); 582 } 583 584 // Finish activity on secondary display. 585 mBroadcastActionTrigger.finishBroadcastReceiverActivity(); 586 587 if (lockScreenSession != null) { 588 // Unlock and check if the focus is switched back to primary display. 589 lockScreenSession.wakeUpDevice().unlockDevice(); 590 } 591 592 waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, DEFAULT_DISPLAY, 593 "Top activity must be switched back to primary display"); 594 } 595 596 /** 597 * Tests that input events on the primary display take focus from the virtual display. 598 */ 599 @Test testStackFocusSwitchOnTouchEvent()600 public void testStackFocusSwitchOnTouchEvent() throws Exception { 601 // If config_perDisplayFocusEnabled, the focus will not move even if touching on 602 // the Activity in the different display. 603 assumeFalse(perDisplayFocusEnabled()); 604 605 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 606 // Create new virtual display. 607 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 608 609 mAmWmState.computeState(VIRTUAL_DISPLAY_ACTIVITY); 610 mAmWmState.assertFocusedActivity("Top activity must be the latest launched one", 611 VIRTUAL_DISPLAY_ACTIVITY); 612 613 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 614 615 waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId, 616 "Activity launched on secondary display must be on top"); 617 618 tapOnDisplayCenter(DEFAULT_DISPLAY); 619 620 waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, DEFAULT_DISPLAY, 621 "Top activity must be on the primary display"); 622 mAmWmState.assertResumedActivities("Both displays must have resumed activities", 623 new SparseArray<ComponentName>(){{ 624 put(DEFAULT_DISPLAY, VIRTUAL_DISPLAY_ACTIVITY); 625 put(newDisplay.mId, TEST_ACTIVITY); 626 }} 627 ); 628 mAmWmState.assertFocusedAppOnDisplay("App on secondary display must still be focused", 629 TEST_ACTIVITY, newDisplay.mId); 630 } 631 } 632 633 634 /** 635 * Tests that tapping on the primary display after showing the keyguard resumes the 636 * activity on the primary display. 637 */ 638 @Test testStackFocusSwitchOnTouchEventAfterKeyguard()639 public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception { 640 assumeFalse(perDisplayFocusEnabled()); 641 assumeTrue(supportsLockScreen()); 642 643 // Launch something on the primary display so we know there is a resumed activity there 644 launchActivity(RESIZEABLE_ACTIVITY); 645 waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY, 646 "Activity launched on primary display must be resumed"); 647 648 try (final LockScreenSession lockScreenSession = new LockScreenSession(); 649 final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) { 650 lockScreenSession.sleepDevice(); 651 652 // Make sure there is no resumed activity when the primary display is off 653 waitAndAssertActivityState(RESIZEABLE_ACTIVITY, STATE_STOPPED, 654 "Activity launched on primary display must be stopped after turning off"); 655 assertEquals("Unexpected resumed activity", 656 0, mAmWmState.getAmState().getResumedActivitiesCount()); 657 658 final ActivityDisplay newDisplay = externalDisplaySession 659 .setCanShowWithInsecureKeyguard(true).createVirtualDisplay(); 660 661 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 662 663 // Unlock the device and tap on the middle of the primary display 664 lockScreenSession.wakeUpDevice(); 665 executeShellCommand("wm dismiss-keyguard"); 666 mAmWmState.waitForKeyguardGone(); 667 mAmWmState.waitForValidState(RESIZEABLE_ACTIVITY, TEST_ACTIVITY); 668 669 // Check that the test activity is resumed on the external display and is on top 670 waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId, 671 "Activity on external display must be resumed and on top"); 672 mAmWmState.assertResumedActivities("Both displays should have resumed activities", 673 new SparseArray<ComponentName>(){{ 674 put(DEFAULT_DISPLAY, RESIZEABLE_ACTIVITY); 675 put(newDisplay.mId, TEST_ACTIVITY); 676 }} 677 ); 678 679 tapOnDisplayCenter(DEFAULT_DISPLAY); 680 681 // Check that the activity on the primary display is the topmost resumed 682 waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY, 683 "Activity on primary display must be resumed and on top"); 684 mAmWmState.assertResumedActivities("Both displays should have resumed activities", 685 new SparseArray<ComponentName>(){{ 686 put(DEFAULT_DISPLAY, RESIZEABLE_ACTIVITY); 687 put(newDisplay.mId, TEST_ACTIVITY); 688 }} 689 ); 690 mAmWmState.assertFocusedAppOnDisplay("App on external display must still be focused", 691 TEST_ACTIVITY, newDisplay.mId); 692 } 693 } 694 695 /** 696 * Tests that showWhenLocked works on a secondary display. 697 */ 698 @Test testSecondaryDisplayShowWhenLocked()699 public void testSecondaryDisplayShowWhenLocked() throws Exception { 700 assumeTrue(supportsSecureLock()); 701 702 try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession(); 703 final LockScreenSession lockScreenSession = new LockScreenSession()) { 704 lockScreenSession.setLockCredential(); 705 706 launchActivity(TEST_ACTIVITY); 707 708 final ActivityDisplay newDisplay = externalDisplaySession.createVirtualDisplay(); 709 launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, newDisplay.mId); 710 711 lockScreenSession.gotoKeyguard(); 712 713 waitAndAssertActivityState(TEST_ACTIVITY, STATE_STOPPED, 714 "Expected stopped activity on default display"); 715 waitAndAssertTopResumedActivity(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, newDisplay.mId, 716 "Expected resumed activity on secondary display"); 717 } 718 } 719 720 /** 721 * Tests tap and set focus between displays. 722 */ 723 @Test testSecondaryDisplayFocus()724 public void testSecondaryDisplayFocus() throws Exception { 725 assumeFalse(perDisplayFocusEnabled()); 726 727 try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) { 728 launchActivity(TEST_ACTIVITY); 729 mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED); 730 731 final ActivityDisplay newDisplay = externalDisplaySession.createVirtualDisplay(); 732 launchActivityOnDisplay(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId); 733 waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId, 734 "Virtual activity should be Top Resumed Activity."); 735 mAmWmState.assertFocusedAppOnDisplay("Activity on second display must be focused.", 736 VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId); 737 738 tapOnDisplayCenter(DEFAULT_DISPLAY); 739 740 waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY, 741 "Activity should be top resumed when tapped."); 742 mAmWmState.assertFocusedActivity("Activity on default display must be top focused.", 743 TEST_ACTIVITY); 744 745 tapOnDisplayCenter(newDisplay.mId); 746 747 waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId, 748 "Virtual display activity should be top resumed when tapped."); 749 mAmWmState.assertFocusedActivity("Activity on second display must be top focused.", 750 VIRTUAL_DISPLAY_ACTIVITY); 751 mAmWmState.assertFocusedAppOnDisplay( 752 "Activity on default display must be still focused.", 753 TEST_ACTIVITY, DEFAULT_DISPLAY); 754 } 755 } 756 757 /** 758 * Tests that toast works on a secondary display. 759 */ 760 @Test 761 @FlakyTest(bugId = 131005232) testSecondaryDisplayShowToast()762 public void testSecondaryDisplayShowToast() throws Exception { 763 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 764 final ActivityDisplay newDisplay = 765 virtualDisplaySession.setPublicDisplay(true).createDisplay(); 766 final String TOAST_NAME = "Toast"; 767 launchActivityOnDisplay(TOAST_ACTIVITY, newDisplay.mId); 768 waitAndAssertTopResumedActivity(TOAST_ACTIVITY, newDisplay.mId, 769 "Activity launched on external display must be resumed"); 770 mAmWmState.waitForWithWmState((state) -> state.containsWindow(TOAST_NAME), 771 "Waiting for toast window to show"); 772 773 assertTrue("Toast window must be shown", 774 mAmWmState.getWmState().containsWindow(TOAST_NAME)); 775 assertTrue("Toast window must be visible", 776 mAmWmState.getWmState().isWindowVisible(TOAST_NAME)); 777 } 778 } 779 780 /** 781 * Tests that the surface size of a fullscreen task is same as its display's surface size. 782 * Also check that the surface size has updated after reparenting to other display. 783 */ 784 @Test testTaskSurfaceSizeAfterReparentDisplay()785 public void testTaskSurfaceSizeAfterReparentDisplay() throws Exception { 786 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 787 // Create new simulated display and launch an activity on it. 788 final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true) 789 .createDisplay(); 790 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 791 792 waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId, 793 "Top activity must be the newly launched one"); 794 assertTopTaskSameSurfaceSizeWithDisplay(newDisplay.mId); 795 796 separateTestJournal(); 797 // Destroy the display. 798 } 799 800 // Activity must be reparented to default display and relaunched. 801 assertActivityLifecycle(TEST_ACTIVITY, true /* relaunched */); 802 waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY, 803 "Top activity must be reparented to default display"); 804 805 // Check the surface size after task was reparented to default display. 806 assertTopTaskSameSurfaceSizeWithDisplay(DEFAULT_DISPLAY); 807 } 808 assertTopTaskSameSurfaceSizeWithDisplay(int displayId)809 private void assertTopTaskSameSurfaceSizeWithDisplay(int displayId) { 810 final WindowManagerState.Display display = mAmWmState.getWmState().getDisplay(displayId); 811 final int stackId = mAmWmState.getWmState().getFrontStackId(displayId); 812 final WindowManagerState.WindowTask task = 813 mAmWmState.getWmState().getStack(stackId).mTasks.get(0); 814 815 assertEquals("Task must have same surface width with its display", 816 display.getSurfaceSize(), task.getSurfaceWidth()); 817 assertEquals("Task must have same surface height with its display", 818 display.getSurfaceSize(), task.getSurfaceHeight()); 819 } 820 821 @Test testAppTransitionForActivityOnDifferentDisplay()822 public void testAppTransitionForActivityOnDifferentDisplay() throws Exception { 823 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession(); 824 final TestActivitySession<StandardActivity> transitionActivitySession = new 825 TestActivitySession<>()) { 826 // Create new simulated display. 827 final ActivityDisplay newDisplay = virtualDisplaySession 828 .setSimulateDisplay(true).createDisplay(); 829 830 // Launch BottomActivity on top of launcher activity to prevent transition state 831 // affected by wallpaper theme. 832 launchActivityOnDisplay(BOTTOM_ACTIVITY, DEFAULT_DISPLAY); 833 waitAndAssertTopResumedActivity(BOTTOM_ACTIVITY, DEFAULT_DISPLAY, 834 "Activity must be resumed"); 835 836 // Launch StandardActivity on default display, verify last transition if is correct. 837 transitionActivitySession.launchTestActivityOnDisplaySync(StandardActivity.class, 838 DEFAULT_DISPLAY); 839 mAmWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY); 840 mAmWmState.assertSanity(); 841 assertEquals(TRANSIT_TASK_OPEN, 842 mAmWmState.getWmState().getDisplay(DEFAULT_DISPLAY).getLastTransition()); 843 844 // Finish current activity & launch another TestActivity in virtual display in parallel. 845 transitionActivitySession.finishCurrentActivityNoWait(); 846 launchActivityOnDisplayNoWait(TEST_ACTIVITY, newDisplay.mId); 847 mAmWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY); 848 mAmWmState.waitForAppTransitionIdleOnDisplay(newDisplay.mId); 849 mAmWmState.assertSanity(); 850 851 // Verify each display's last transition if is correct as expected. 852 assertEquals(TRANSIT_TASK_CLOSE, 853 mAmWmState.getWmState().getDisplay(DEFAULT_DISPLAY).getLastTransition()); 854 assertEquals(TRANSIT_TASK_OPEN, 855 mAmWmState.getWmState().getDisplay(newDisplay.mId).getLastTransition()); 856 } 857 } 858 859 @Test testNoTransitionWhenMovingActivityToDisplay()860 public void testNoTransitionWhenMovingActivityToDisplay() throws Exception { 861 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 862 // Create new simulated display & capture new display's transition state. 863 final ActivityDisplay newDisplay = virtualDisplaySession 864 .setSimulateDisplay(true).createDisplay(); 865 866 // Launch TestActivity in virtual display & capture its transition state. 867 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 868 mAmWmState.waitForAppTransitionIdleOnDisplay(newDisplay.mId); 869 mAmWmState.assertSanity(); 870 final String lastTranstionOnVirtualDisplay = mAmWmState.getWmState() 871 .getDisplay(newDisplay.mId).getLastTransition(); 872 873 // Move TestActivity from virtual display to default display. 874 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY) 875 .allowMultipleInstances(false).setNewTask(true) 876 .setDisplayId(DEFAULT_DISPLAY).execute(); 877 878 // Verify TestActivity moved to virtual display. 879 waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY, 880 "Existing task must be brought to front"); 881 882 // Make sure last transition will not change when task move to another display. 883 assertEquals(lastTranstionOnVirtualDisplay, 884 mAmWmState.getWmState().getDisplay(newDisplay.mId).getLastTransition()); 885 } 886 } 887 888 @Test testPreQTopProcessResumedActivity()889 public void testPreQTopProcessResumedActivity() throws Exception { 890 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 891 final ActivityDisplay newDisplay = 892 virtualDisplaySession.setSimulateDisplay(true).createDisplay(); 893 894 getLaunchActivityBuilder().setUseInstrumentation() 895 .setTargetActivity(SDK_27_TEST_ACTIVITY).setNewTask(true) 896 .setDisplayId(newDisplay.mId).execute(); 897 waitAndAssertTopResumedActivity(SDK_27_TEST_ACTIVITY, newDisplay.mId, 898 "Activity launched on secondary display must be resumed and focused"); 899 900 getLaunchActivityBuilder().setUseInstrumentation() 901 .setTargetActivity(SDK_27_LAUNCHING_ACTIVITY).setNewTask(true) 902 .setDisplayId(DEFAULT_DISPLAY).execute(); 903 waitAndAssertTopResumedActivity(SDK_27_LAUNCHING_ACTIVITY, DEFAULT_DISPLAY, 904 "Activity launched on default display must be resumed and focused"); 905 906 assertEquals("There must be only one resumed activity in the package.", 1, 907 mAmWmState.getAmState().getResumedActivitiesCountInPackage( 908 SDK_27_LAUNCHING_ACTIVITY.getPackageName())); 909 910 getLaunchActivityBuilder().setUseInstrumentation() 911 .setTargetActivity(SDK_27_SEPARATE_PROCESS_ACTIVITY).setNewTask(true) 912 .setDisplayId(DEFAULT_DISPLAY).execute(); 913 waitAndAssertTopResumedActivity(SDK_27_SEPARATE_PROCESS_ACTIVITY, DEFAULT_DISPLAY, 914 "Activity launched on default display must be resumed and focused"); 915 assertTrue("Activity that was on secondary display must be resumed", 916 mAmWmState.getAmState().hasActivityState(SDK_27_TEST_ACTIVITY, STATE_RESUMED)); 917 assertEquals("There must be only two resumed activities in the package.", 2, 918 mAmWmState.getAmState().getResumedActivitiesCountInPackage( 919 SDK_27_TEST_ACTIVITY.getPackageName())); 920 } 921 } 922 923 } 924