1/* 2 * Copyright (C) 2016 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 17package android.server.am; 18 19import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 20import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; 21import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; 22import static android.server.am.ActivityLauncher.KEY_LAUNCH_ACTIVITY; 23import static android.server.am.ActivityLauncher.KEY_NEW_TASK; 24import static android.server.am.ActivityLauncher.KEY_TARGET_COMPONENT; 25import static android.server.am.ActivityManagerDisplayTestBase.ReportedDisplayMetrics.getDisplayMetrics; 26import static android.server.am.ActivityManagerState.STATE_RESUMED; 27import static android.server.am.ActivityManagerState.STATE_STOPPED; 28import static android.server.am.ComponentNameUtils.getActivityName; 29import static android.server.am.ComponentNameUtils.getWindowName; 30import static android.server.am.Components.ALT_LAUNCHING_ACTIVITY; 31import static android.server.am.Components.BROADCAST_RECEIVER_ACTIVITY; 32import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST; 33import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST; 34import static android.server.am.Components.LAUNCHING_ACTIVITY; 35import static android.server.am.Components.LAUNCH_BROADCAST_ACTION; 36import static android.server.am.Components.LAUNCH_BROADCAST_RECEIVER; 37import static android.server.am.Components.NON_RESIZEABLE_ACTIVITY; 38import static android.server.am.Components.RESIZEABLE_ACTIVITY; 39import static android.server.am.Components.SHOW_WHEN_LOCKED_ATTR_ACTIVITY; 40import static android.server.am.Components.TEST_ACTIVITY; 41import static android.server.am.Components.VIRTUAL_DISPLAY_ACTIVITY; 42import static android.server.am.StateLogger.logAlways; 43import static android.server.am.StateLogger.logE; 44import static android.server.am.UiDeviceUtils.pressSleepButton; 45import static android.server.am.UiDeviceUtils.pressWakeupButton; 46import static android.server.am.second.Components.SECOND_ACTIVITY; 47import static android.server.am.second.Components.SECOND_LAUNCH_BROADCAST_ACTION; 48import static android.server.am.second.Components.SECOND_LAUNCH_BROADCAST_RECEIVER; 49import static android.server.am.second.Components.SECOND_NO_EMBEDDING_ACTIVITY; 50import static android.server.am.third.Components.THIRD_ACTIVITY; 51import static android.view.Display.DEFAULT_DISPLAY; 52 53import static org.junit.Assert.assertEquals; 54import static org.junit.Assert.assertFalse; 55import static org.junit.Assert.assertNotNull; 56import static org.junit.Assert.assertNull; 57import static org.junit.Assert.assertTrue; 58import static org.junit.Assert.fail; 59import static org.junit.Assume.assumeTrue; 60 61import android.content.ComponentName; 62import android.os.SystemClock; 63import android.platform.test.annotations.Presubmit; 64import android.server.am.ActivityManagerState.ActivityDisplay; 65import android.support.test.filters.FlakyTest; 66 67import androidx.annotation.Nullable; 68 69import org.junit.Before; 70import org.junit.Test; 71 72import java.util.concurrent.TimeUnit; 73import java.util.List; 74import java.util.regex.Matcher; 75import java.util.regex.Pattern; 76 77/** 78 * Build/Install/Run: 79 * atest CtsActivityManagerDeviceTestCases:ActivityManagerMultiDisplayTests 80 */ 81@Presubmit 82@FlakyTest(bugId = 77652261) 83public class ActivityManagerMultiDisplayTests extends ActivityManagerDisplayTestBase { 84 85 // TODO(b/70247058): Use {@link Context#sendBroadcast(Intent). 86 // Shell command to finish {@link #BROADCAST_RECEIVER_ACTIVITY}. 87 private static final String FINISH_ACTIVITY_BROADCAST = "am broadcast -a " 88 + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_FINISH_BROADCAST + " true"; 89 // Shell command to launch activity via {@link #BROADCAST_RECEIVER_ACTIVITY}. 90 private static final String LAUNCH_ACTIVITY_BROADCAST = "am broadcast -a " 91 + ACTION_TRIGGER_BROADCAST + " --ez " + KEY_LAUNCH_ACTIVITY + " true --ez " 92 + KEY_NEW_TASK + " true --es " + KEY_TARGET_COMPONENT + " "; 93 94 @Before 95 @Override 96 public void setUp() throws Exception { 97 super.setUp(); 98 99 assumeTrue(supportsMultiDisplay()); 100 } 101 102 /** 103 * Tests launching an activity on virtual display. 104 */ 105 @Test 106 public void testLaunchActivityOnSecondaryDisplay() throws Exception { 107 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 108 // Create new virtual display. 109 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 110 111 // Launch activity on new secondary display. 112 final LogSeparator logSeparator = separateLogs(); 113 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 114 mAmWmState.computeState(TEST_ACTIVITY); 115 116 mAmWmState.assertFocusedActivity( 117 "Activity launched on secondary display must be focused", 118 TEST_ACTIVITY); 119 120 // Check that activity is on the right display. 121 final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 122 final ActivityManagerState.ActivityStack frontStack = 123 mAmWmState.getAmState().getStackById(frontStackId); 124 assertEquals("Launched activity must be on the secondary display and resumed", 125 getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity); 126 mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId); 127 128 // Check that activity config corresponds to display config. 129 final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY, 130 logSeparator); 131 assertEquals("Activity launched on secondary display must have proper configuration", 132 CUSTOM_DENSITY_DPI, reportedSizes.densityDpi); 133 } 134 } 135 136 /** 137 * Tests launching an activity on primary display explicitly. 138 */ 139 @Test 140 public void testLaunchActivityOnPrimaryDisplay() throws Exception { 141 // Launch activity on primary display explicitly. 142 launchActivityOnDisplay(LAUNCHING_ACTIVITY, 0); 143 mAmWmState.computeState(LAUNCHING_ACTIVITY); 144 145 mAmWmState.assertFocusedActivity("Activity launched on primary display must be focused", 146 LAUNCHING_ACTIVITY); 147 148 // Check that activity is on the right display. 149 int frontStackId = mAmWmState.getAmState().getFrontStackId(0 /* displayId */); 150 ActivityManagerState.ActivityStack frontStack 151 = mAmWmState.getAmState().getStackById(frontStackId); 152 assertEquals("Launched activity must be on the primary display and resumed", 153 getActivityName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity); 154 mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId); 155 156 // Launch another activity on primary display using the first one 157 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).setNewTask(true) 158 .setMultipleTask(true).setDisplayId(0).execute(); 159 mAmWmState.computeState(TEST_ACTIVITY); 160 161 mAmWmState.assertFocusedActivity("Activity launched on primary display must be focused", 162 TEST_ACTIVITY); 163 164 // Check that activity is on the right display. 165 frontStackId = mAmWmState.getAmState().getFrontStackId(0 /* displayId */); 166 frontStack = mAmWmState.getAmState().getStackById(frontStackId); 167 assertEquals("Launched activity must be on the primary display and resumed", 168 getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity); 169 mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId); 170 } 171 172 /** 173 * Tests launching a non-resizeable activity on virtual display. It should land on the 174 * default display. 175 */ 176 @Test 177 public void testLaunchNonResizeableActivityOnSecondaryDisplay() throws Exception { 178 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 179 // Create new virtual display. 180 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 181 182 // Launch activity on new secondary display. 183 launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY, newDisplay.mId); 184 mAmWmState.computeState(NON_RESIZEABLE_ACTIVITY); 185 186 mAmWmState.assertFocusedActivity( 187 "Activity launched on secondary display must be focused", 188 NON_RESIZEABLE_ACTIVITY); 189 190 // Check that activity is on the right display. 191 final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY); 192 final ActivityManagerState.ActivityStack frontStack = 193 mAmWmState.getAmState().getStackById(frontStackId); 194 assertEquals("Launched activity must be on the primary display and resumed", 195 getActivityName(NON_RESIZEABLE_ACTIVITY), 196 frontStack.mResumedActivity); 197 mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId); 198 } 199 } 200 201 /** 202 * Tests launching a non-resizeable activity on virtual display while split-screen is active 203 * on the primary display. It should land on the primary display and dismiss docked stack. 204 */ 205 @Test 206 public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception { 207 assumeTrue(supportsSplitScreenMultiWindow()); 208 209 // Start launching activity. 210 launchActivitiesInSplitScreen( 211 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY), 212 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)); 213 214 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 215 // Create new virtual display. 216 final ActivityDisplay newDisplay = virtualDisplaySession.setLaunchInSplitScreen(true) 217 .createDisplay(); 218 219 // Launch activity on new secondary display. 220 launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY, newDisplay.mId); 221 mAmWmState.computeState(NON_RESIZEABLE_ACTIVITY); 222 223 mAmWmState.assertFocusedActivity( 224 "Activity launched on secondary display must be focused", 225 NON_RESIZEABLE_ACTIVITY); 226 227 // Check that activity is on the right display. 228 final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY); 229 final ActivityManagerState.ActivityStack frontStack = 230 mAmWmState.getAmState().getStackById(frontStackId); 231 assertEquals("Launched activity must be on the primary display and resumed", 232 getActivityName(NON_RESIZEABLE_ACTIVITY), 233 frontStack.mResumedActivity); 234 mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId); 235 mAmWmState.assertDoesNotContainStack("Must not contain docked stack.", 236 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD); 237 } 238 } 239 240 /** 241 * Tests moving a non-resizeable activity to a virtual display. It should stay on the default 242 * display with no action performed. 243 */ 244 @Test 245 public void testMoveNonResizeableActivityToSecondaryDisplay() throws Exception { 246 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 247 // Create new virtual display. 248 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 249 // Launch a non-resizeable activity on a primary display. 250 launchActivityInNewTask(NON_RESIZEABLE_ACTIVITY); 251 // Launch a resizeable activity on new secondary display to create a new stack there. 252 launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId); 253 final int externalFrontStackId = mAmWmState.getAmState() 254 .getFrontStackId(newDisplay.mId); 255 256 // Try to move the non-resizeable activity to new secondary display. 257 moveActivityToStack(NON_RESIZEABLE_ACTIVITY, externalFrontStackId); 258 mAmWmState.computeState(NON_RESIZEABLE_ACTIVITY); 259 260 mAmWmState.assertFocusedActivity( 261 "Activity launched on secondary display must be focused", 262 RESIZEABLE_ACTIVITY); 263 264 // Check that activity is in the same stack 265 final int defaultFrontStackId = mAmWmState.getAmState().getFrontStackId( 266 DEFAULT_DISPLAY); 267 final ActivityManagerState.ActivityStack defaultFrontStack = 268 mAmWmState.getAmState().getStackById(defaultFrontStackId); 269 assertEquals("Launched activity must be on the primary display and resumed", 270 getActivityName(NON_RESIZEABLE_ACTIVITY), 271 defaultFrontStack.getTopTask().mRealActivity); 272 mAmWmState.assertFocusedStack("Focus must remain on the secondary display", 273 externalFrontStackId); 274 } 275 } 276 277 /** 278 * Tests launching a non-resizeable activity on virtual display from activity there. It should 279 * land on the secondary display based on the resizeability of the root activity of the task. 280 */ 281 @Test 282 public void testLaunchNonResizeableActivityFromSecondaryDisplaySameTask() throws Exception { 283 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 284 // Create new simulated display. 285 final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true) 286 .createDisplay(); 287 288 // Launch activity on new secondary display. 289 launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId); 290 mAmWmState.assertFocusedActivity( 291 "Activity launched on secondary display must be focused", 292 BROADCAST_RECEIVER_ACTIVITY); 293 294 // Check that launching activity is on the secondary display. 295 int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 296 ActivityManagerState.ActivityStack frontStack = 297 mAmWmState.getAmState().getStackById(frontStackId); 298 assertEquals("Launched activity must be on the secondary display and resumed", 299 getActivityName(BROADCAST_RECEIVER_ACTIVITY), 300 frontStack.mResumedActivity); 301 mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId); 302 303 // Launch non-resizeable activity from secondary display. 304 executeShellCommand( 305 LAUNCH_ACTIVITY_BROADCAST + getActivityName(NON_RESIZEABLE_ACTIVITY)); 306 mAmWmState.computeState(NON_RESIZEABLE_ACTIVITY); 307 308 // Check that non-resizeable activity is on the secondary display, because of the 309 // resizeable root of the task. 310 frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 311 frontStack = mAmWmState.getAmState().getStackById(frontStackId); 312 assertEquals("Launched activity must be on the primary display and resumed", 313 getActivityName(NON_RESIZEABLE_ACTIVITY), 314 frontStack.mResumedActivity); 315 mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId); 316 } 317 } 318 319 /** 320 * Tests launching a non-resizeable activity on virtual display from activity there. It should 321 * land on some different suitable display (usually - on the default one). 322 */ 323 @Test 324 public void testLaunchNonResizeableActivityFromSecondaryDisplayNewTask() throws Exception { 325 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 326 // Create new virtual display. 327 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 328 329 // Launch activity on new secondary display. 330 launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); 331 mAmWmState.assertFocusedActivity( 332 "Activity launched on secondary display must be focused", 333 LAUNCHING_ACTIVITY); 334 335 // Check that launching activity is on the secondary display. 336 int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 337 ActivityManagerState.ActivityStack frontStack = 338 mAmWmState.getAmState().getStackById(frontStackId); 339 assertEquals("Launched activity must be on the secondary display and resumed", 340 getActivityName(LAUNCHING_ACTIVITY), 341 frontStack.mResumedActivity); 342 mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId); 343 344 // Launch non-resizeable activity from secondary display. 345 getLaunchActivityBuilder().setTargetActivity(NON_RESIZEABLE_ACTIVITY) 346 .setNewTask(true).setMultipleTask(true).execute(); 347 348 // Check that non-resizeable activity is on the primary display. 349 frontStackId = mAmWmState.getAmState().getFocusedStackId(); 350 frontStack = mAmWmState.getAmState().getStackById(frontStackId); 351 assertFalse("Launched activity must be on a different display", 352 newDisplay.mId == frontStack.mDisplayId); 353 assertEquals("Launched activity must be resumed", 354 getActivityName(NON_RESIZEABLE_ACTIVITY), 355 frontStack.mResumedActivity); 356 mAmWmState.assertFocusedStack("Focus must be on a just launched activity", 357 frontStackId); 358 } 359 } 360 361 /** 362 * Tests launching an activity on a virtual display without special permission must not be 363 * allowed. 364 */ 365 @Test 366 public void testLaunchWithoutPermissionOnVirtualDisplay() throws Exception { 367 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 368 // Create new virtual display. 369 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 370 371 final LogSeparator logSeparator = separateLogs(); 372 373 // Try to launch an activity and check it security exception was triggered. 374 getLaunchActivityBuilder() 375 .setUseBroadcastReceiver(SECOND_LAUNCH_BROADCAST_RECEIVER, 376 SECOND_LAUNCH_BROADCAST_ACTION) 377 .setDisplayId(newDisplay.mId) 378 .setTargetActivity(TEST_ACTIVITY) 379 .execute(); 380 381 assertSecurityException("ActivityLauncher", logSeparator); 382 383 mAmWmState.computeState(TEST_ACTIVITY); 384 assertFalse("Restricted activity must not be launched", 385 mAmWmState.getAmState().containsActivity(TEST_ACTIVITY)); 386 } 387 } 388 389 /** 390 * Tests launching an activity on a virtual display without special permission must be allowed 391 * for activities with same UID. 392 */ 393 @Test 394 public void testLaunchWithoutPermissionOnVirtualDisplayByOwner() throws Exception { 395 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 396 // Create new virtual display. 397 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 398 399 // Try to launch an activity and check it security exception was triggered. 400 getLaunchActivityBuilder() 401 .setUseBroadcastReceiver(LAUNCH_BROADCAST_RECEIVER, LAUNCH_BROADCAST_ACTION) 402 .setDisplayId(newDisplay.mId) 403 .setTargetActivity(TEST_ACTIVITY) 404 .execute(); 405 406 mAmWmState.waitForValidState(TEST_ACTIVITY); 407 408 final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId(); 409 final ActivityManagerState.ActivityStack focusedStack = 410 mAmWmState.getAmState().getStackById(externalFocusedStackId); 411 assertEquals("Focused stack must be on secondary display", newDisplay.mId, 412 focusedStack.mDisplayId); 413 414 mAmWmState.assertFocusedActivity("Focus must be on newly launched app", 415 TEST_ACTIVITY); 416 assertEquals("Activity launched by owner must be on external display", 417 externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId()); 418 } 419 } 420 421 /** 422 * Tests launching an activity on virtual display and then launching another activity via shell 423 * command and without specifying the display id - the second activity must appear on the 424 * primary display. 425 */ 426 @Test 427 public void testConsequentLaunchActivity() throws Exception { 428 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 429 // Create new virtual display. 430 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 431 432 // Launch activity on new secondary display. 433 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 434 mAmWmState.computeState(TEST_ACTIVITY); 435 436 mAmWmState.assertFocusedActivity( 437 "Activity launched on secondary display must be focused", 438 TEST_ACTIVITY); 439 440 // Launch second activity without specifying display. 441 launchActivity(LAUNCHING_ACTIVITY); 442 mAmWmState.computeState(LAUNCHING_ACTIVITY); 443 444 // Check that activity is launched in focused stack on primary display. 445 mAmWmState.assertFocusedActivity("Launched activity must be focused", 446 LAUNCHING_ACTIVITY); 447 final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY); 448 final ActivityManagerState.ActivityStack frontStack = 449 mAmWmState.getAmState().getStackById(frontStackId); 450 assertEquals("Launched activity must be resumed in front stack", 451 getActivityName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity); 452 assertEquals("Front stack must be on primary display", 453 DEFAULT_DISPLAY, frontStack.mDisplayId); 454 } 455 } 456 457 /** 458 * Tests launching an activity on simulated display and then launching another activity from the 459 * first one - it must appear on the secondary display, because it was launched from there. 460 */ 461 @Test 462 public void testConsequentLaunchActivityFromSecondaryDisplay() throws Exception { 463 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 464 // Create new simulated display. 465 final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true) 466 .createDisplay(); 467 468 // Launch activity on new secondary display. 469 launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); 470 mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY)); 471 472 mAmWmState.assertFocusedActivity( 473 "Activity launched on secondary display must be resumed", 474 LAUNCHING_ACTIVITY); 475 476 // Launch second activity from app on secondary display without specifying display id. 477 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute(); 478 mAmWmState.computeState(TEST_ACTIVITY); 479 480 // Check that activity is launched in focused stack on external display. 481 mAmWmState.assertFocusedActivity("Launched activity must be focused", 482 TEST_ACTIVITY); 483 final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 484 final ActivityManagerState.ActivityStack frontStack = 485 mAmWmState.getAmState().getStackById(frontStackId); 486 assertEquals("Launched activity must be resumed in front stack", 487 getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity); 488 } 489 } 490 491 /** 492 * Tests launching an activity on virtual display and then launching another activity from the 493 * first one - it must appear on the secondary display, because it was launched from there. 494 */ 495 @Test 496 public void testConsequentLaunchActivityFromVirtualDisplay() throws Exception { 497 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 498 // Create new virtual display. 499 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 500 501 // Launch activity on new secondary display. 502 launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); 503 mAmWmState.computeState(LAUNCHING_ACTIVITY); 504 505 mAmWmState.assertFocusedActivity( 506 "Activity launched on secondary display must be resumed", 507 LAUNCHING_ACTIVITY); 508 509 // Launch second activity from app on secondary display without specifying display id. 510 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute(); 511 mAmWmState.computeState(TEST_ACTIVITY); 512 513 // Check that activity is launched in focused stack on external display. 514 mAmWmState.assertFocusedActivity("Launched activity must be focused", 515 TEST_ACTIVITY); 516 final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 517 final ActivityManagerState.ActivityStack frontStack = mAmWmState.getAmState() 518 .getStackById(frontStackId); 519 assertEquals("Launched activity must be resumed in front stack", 520 getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity); 521 } 522 } 523 524 /** 525 * Tests launching an activity on virtual display and then launching another activity from the 526 * first one with specifying the target display - it must appear on the secondary display. 527 */ 528 @Test 529 public void testConsequentLaunchActivityFromVirtualDisplayToTargetDisplay() throws Exception { 530 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 531 // Create new virtual display. 532 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 533 534 // Launch activity on new secondary display. 535 launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); 536 mAmWmState.computeState(LAUNCHING_ACTIVITY); 537 538 mAmWmState.assertFocusedActivity( 539 "Activity launched on secondary display must be resumed", 540 LAUNCHING_ACTIVITY); 541 542 // Launch second activity from app on secondary display specifying same display id. 543 getLaunchActivityBuilder() 544 .setTargetActivity(SECOND_ACTIVITY) 545 .setDisplayId(newDisplay.mId) 546 .execute(); 547 mAmWmState.computeState(TEST_ACTIVITY); 548 549 // Check that activity is launched in focused stack on external display. 550 mAmWmState.assertFocusedActivity("Launched activity must be focused", SECOND_ACTIVITY); 551 int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 552 ActivityManagerState.ActivityStack frontStack = 553 mAmWmState.getAmState().getStackById(frontStackId); 554 assertEquals("Launched activity must be resumed in front stack", 555 getActivityName(SECOND_ACTIVITY), frontStack.mResumedActivity); 556 557 // Launch other activity with different uid and check if it has launched successfully. 558 getLaunchActivityBuilder() 559 .setUseBroadcastReceiver(SECOND_LAUNCH_BROADCAST_RECEIVER, 560 SECOND_LAUNCH_BROADCAST_ACTION) 561 .setDisplayId(newDisplay.mId) 562 .setTargetActivity(THIRD_ACTIVITY) 563 .execute(); 564 mAmWmState.waitForValidState(THIRD_ACTIVITY); 565 566 // Check that activity is launched in focused stack on external display. 567 mAmWmState.assertFocusedActivity("Launched activity must be focused", THIRD_ACTIVITY); 568 frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 569 frontStack = mAmWmState.getAmState().getStackById(frontStackId); 570 assertEquals("Launched activity must be resumed in front stack", 571 getActivityName(THIRD_ACTIVITY), frontStack.mResumedActivity); 572 } 573 } 574 575 /** 576 * Tests launching an activity on virtual display and then launching another activity that 577 * doesn't allow embedding - it should fail with security exception. 578 */ 579 @Test 580 public void testConsequentLaunchActivityFromVirtualDisplayNoEmbedding() throws Exception { 581 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 582 // Create new virtual display. 583 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 584 585 // Launch activity on new secondary display. 586 launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); 587 mAmWmState.computeState(LAUNCHING_ACTIVITY); 588 589 mAmWmState.assertFocusedActivity( 590 "Activity launched on secondary display must be resumed", 591 LAUNCHING_ACTIVITY); 592 593 final LogSeparator logSeparator = separateLogs(); 594 595 // Launch second activity from app on secondary display specifying same display id. 596 getLaunchActivityBuilder() 597 .setTargetActivity(SECOND_NO_EMBEDDING_ACTIVITY) 598 .setDisplayId(newDisplay.mId) 599 .execute(); 600 601 assertSecurityException("ActivityLauncher", logSeparator); 602 } 603 } 604 605 /** 606 * Tests launching an activity to secondary display from activity on primary display. 607 */ 608 @Test 609 public void testLaunchActivityFromAppToSecondaryDisplay() throws Exception { 610 // Start launching activity. 611 launchActivity(LAUNCHING_ACTIVITY); 612 613 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 614 // Create new simulated display. 615 final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true) 616 .createDisplay(); 617 618 // Launch activity on secondary display from the app on primary display. 619 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY) 620 .setDisplayId(newDisplay.mId).execute(); 621 622 // Check that activity is launched on external display. 623 mAmWmState.computeState(TEST_ACTIVITY); 624 mAmWmState.assertFocusedActivity( 625 "Activity launched on secondary display must be focused", 626 TEST_ACTIVITY); 627 final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 628 final ActivityManagerState.ActivityStack frontStack = 629 mAmWmState.getAmState().getStackById(frontStackId); 630 assertEquals("Launched activity must be resumed in front stack", 631 getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity); 632 } 633 } 634 635 /** 636 * Tests launching activities on secondary and then on primary display to see if the stack 637 * visibility is not affected. 638 */ 639 @Test 640 public void testLaunchActivitiesAffectsVisibility() throws Exception { 641 // Start launching activity. 642 launchActivity(LAUNCHING_ACTIVITY); 643 644 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 645 // Create new virtual display. 646 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 647 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 648 649 // Launch activity on new secondary display. 650 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 651 mAmWmState.assertVisibility(TEST_ACTIVITY, true /* visible */); 652 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 653 654 // Launch activity on primary display and check if it doesn't affect activity on 655 // secondary display. 656 getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY).execute(); 657 mAmWmState.waitForValidState(RESIZEABLE_ACTIVITY); 658 mAmWmState.assertVisibility(TEST_ACTIVITY, true /* visible */); 659 mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */); 660 } 661 } 662 663 /** 664 * Test that move-task works when moving between displays. 665 */ 666 @Test 667 public void testMoveTaskBetweenDisplays() throws Exception { 668 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 669 // Create new virtual display. 670 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 671 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 672 mAmWmState.assertFocusedActivity("Virtual display activity must be focused", 673 VIRTUAL_DISPLAY_ACTIVITY); 674 final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId(); 675 ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById( 676 defaultDisplayStackId); 677 assertEquals("Focus must remain on primary display", 678 DEFAULT_DISPLAY, focusedStack.mDisplayId); 679 680 // Launch activity on new secondary display. 681 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 682 mAmWmState.assertFocusedActivity("Focus must be on secondary display", 683 TEST_ACTIVITY); 684 int focusedStackId = mAmWmState.getAmState().getFocusedStackId(); 685 focusedStack = mAmWmState.getAmState().getStackById(focusedStackId); 686 assertEquals("Focused stack must be on secondary display", 687 newDisplay.mId, focusedStack.mDisplayId); 688 689 // Move activity from secondary display to primary. 690 moveActivityToStack(TEST_ACTIVITY, defaultDisplayStackId); 691 mAmWmState.waitForFocusedStack(defaultDisplayStackId); 692 mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY); 693 focusedStackId = mAmWmState.getAmState().getFocusedStackId(); 694 focusedStack = mAmWmState.getAmState().getStackById(focusedStackId); 695 assertEquals("Focus must return to primary display", 696 DEFAULT_DISPLAY, focusedStack.mDisplayId); 697 } 698 } 699 700 /** 701 * Tests launching activities on secondary display and then removing it to see if stack focus 702 * is moved correctly. 703 * This version launches virtual display creator to fullscreen stack in split-screen. 704 */ 705 @Test 706 public void testStackFocusSwitchOnDisplayRemoved() throws Exception { 707 assumeTrue(supportsSplitScreenMultiWindow()); 708 709 // Start launching activity into docked stack. 710 launchActivitiesInSplitScreen( 711 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY), 712 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)); 713 mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */); 714 715 tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */, 716 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); 717 } 718 719 /** 720 * Tests launching activities on secondary display and then removing it to see if stack focus 721 * is moved correctly. 722 * This version launches virtual display creator to docked stack in split-screen. 723 */ 724 @Test 725 public void testStackFocusSwitchOnDisplayRemoved2() throws Exception { 726 assumeTrue(supportsSplitScreenMultiWindow()); 727 728 // Setup split-screen. 729 launchActivitiesInSplitScreen( 730 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY), 731 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY)); 732 mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */); 733 734 tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */, 735 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); 736 } 737 738 /** 739 * Tests launching activities on secondary display and then removing it to see if stack focus 740 * is moved correctly. 741 * This version works without split-screen. 742 */ 743 @Test 744 public void testStackFocusSwitchOnDisplayRemoved3() throws Exception { 745 // Start an activity on default display to determine default stack. 746 launchActivity(BROADCAST_RECEIVER_ACTIVITY); 747 final int focusedStackWindowingMode = mAmWmState.getAmState().getFrontStackWindowingMode( 748 DEFAULT_DISPLAY); 749 // Finish probing activity. 750 executeShellCommand(FINISH_ACTIVITY_BROADCAST); 751 752 tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */, 753 focusedStackWindowingMode); 754 } 755 756 /** 757 * Create a virtual display, launch a test activity there, destroy the display and check if test 758 * activity is moved to a stack on the default display. 759 */ 760 private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int windowingMode) 761 throws Exception { 762 LogSeparator logSeparator; 763 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 764 // Create new virtual display. 765 final ActivityDisplay newDisplay = virtualDisplaySession 766 .setPublicDisplay(true) 767 .setLaunchInSplitScreen(splitScreen) 768 .createDisplay(); 769 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 770 if (splitScreen) { 771 mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */); 772 } 773 774 // Launch activity on new secondary display. 775 launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId); 776 mAmWmState.assertFocusedActivity("Focus must be on secondary display", 777 RESIZEABLE_ACTIVITY); 778 final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 779 mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId); 780 781 // Destroy virtual display. 782 logSeparator = separateLogs(); 783 } 784 785 mAmWmState.computeState(true); 786 assertActivityLifecycle(RESIZEABLE_ACTIVITY, false /* relaunched */, logSeparator); 787 mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(RESIZEABLE_ACTIVITY) 788 .setWindowingMode(windowingMode) 789 .setActivityType(ACTIVITY_TYPE_STANDARD) 790 .build()); 791 mAmWmState.assertSanity(); 792 mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */); 793 794 // Check if the focus is switched back to primary display. 795 mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */); 796 mAmWmState.assertFocusedStack( 797 "Default stack on primary display must be focused after display removed", 798 windowingMode, ACTIVITY_TYPE_STANDARD); 799 mAmWmState.assertFocusedActivity( 800 "Focus must be switched back to activity on primary display", 801 RESIZEABLE_ACTIVITY); 802 } 803 804 /** 805 * Tests launching activities on secondary display and then removing it to see if stack focus 806 * is moved correctly. 807 */ 808 @Test 809 public void testStackFocusSwitchOnStackEmptied() throws Exception { 810 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession(); 811 final LockScreenSession lockScreenSession = new LockScreenSession()) { 812 // Create new virtual display. 813 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 814 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 815 final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY); 816 817 // Launch activity on new secondary display. 818 launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId); 819 mAmWmState.assertFocusedActivity("Focus must be on secondary display", 820 BROADCAST_RECEIVER_ACTIVITY); 821 822 // Lock the device, so that activity containers will be detached. 823 lockScreenSession.sleepDevice(); 824 825 // Finish activity on secondary display. 826 executeShellCommand(FINISH_ACTIVITY_BROADCAST); 827 828 // Unlock and check if the focus is switched back to primary display. 829 lockScreenSession.wakeUpDevice() 830 .unlockDevice(); 831 mAmWmState.waitForFocusedStack(focusedStackId); 832 mAmWmState.waitForValidState(VIRTUAL_DISPLAY_ACTIVITY); 833 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 834 mAmWmState.assertFocusedActivity("Focus must be switched back to primary display", 835 VIRTUAL_DISPLAY_ACTIVITY); 836 } 837 } 838 839 /** 840 * Tests that input events on the primary display take focus from the virtual display. 841 */ 842 @Test 843 public void testStackFocusSwitchOnTouchEvent() throws Exception { 844 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 845 // Create new virtual display. 846 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 847 848 mAmWmState.computeState(VIRTUAL_DISPLAY_ACTIVITY); 849 mAmWmState.assertFocusedActivity("Focus must be switched back to primary display", 850 VIRTUAL_DISPLAY_ACTIVITY); 851 852 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 853 854 mAmWmState.computeState(TEST_ACTIVITY); 855 mAmWmState.assertFocusedActivity( 856 "Activity launched on secondary display must be focused", 857 TEST_ACTIVITY); 858 859 final ReportedDisplayMetrics displayMetrics = getDisplayMetrics(); 860 final int width = displayMetrics.getSize().getWidth(); 861 final int height = displayMetrics.getSize().getHeight(); 862 executeShellCommand("input tap " + (width / 2) + " " + (height / 2)); 863 864 mAmWmState.computeState(VIRTUAL_DISPLAY_ACTIVITY); 865 mAmWmState.assertFocusedActivity("Focus must be switched back to primary display", 866 VIRTUAL_DISPLAY_ACTIVITY); 867 } 868 } 869 870 /** Test that shell is allowed to launch on secondary displays. */ 871 @Test 872 public void testPermissionLaunchFromShell() throws Exception { 873 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 874 // Create new virtual display. 875 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 876 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 877 mAmWmState.assertFocusedActivity("Virtual display activity must be focused", 878 VIRTUAL_DISPLAY_ACTIVITY); 879 final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId(); 880 ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById( 881 defaultDisplayFocusedStackId); 882 assertEquals("Focus must remain on primary display", 883 DEFAULT_DISPLAY, focusedStack.mDisplayId); 884 885 // Launch activity on new secondary display. 886 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 887 mAmWmState.assertFocusedActivity("Focus must be on secondary display", 888 TEST_ACTIVITY); 889 final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId(); 890 focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId); 891 assertEquals("Focused stack must be on secondary display", newDisplay.mId, 892 focusedStack.mDisplayId); 893 894 // Launch other activity with different uid and check it is launched on dynamic stack on 895 // secondary display. 896 final String startCmd = "am start -n " + getActivityName(SECOND_ACTIVITY) 897 + " --display " + newDisplay.mId; 898 executeShellCommand(startCmd); 899 900 mAmWmState.waitForValidState(SECOND_ACTIVITY); 901 mAmWmState.assertFocusedActivity( 902 "Focus must be on newly launched app", SECOND_ACTIVITY); 903 assertEquals("Activity launched by system must be on external display", 904 externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId()); 905 } 906 } 907 908 /** Test that launching from app that is on external display is allowed. */ 909 @Test 910 public void testPermissionLaunchFromAppOnSecondary() throws Exception { 911 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 912 // Create new simulated display. 913 final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true) 914 .createDisplay(); 915 916 // Launch activity with different uid on secondary display. 917 final String startCmd = "am start -n " + getActivityName(SECOND_ACTIVITY) 918 + " --display " + newDisplay.mId; 919 executeShellCommand(startCmd); 920 921 mAmWmState.waitForValidState(SECOND_ACTIVITY); 922 mAmWmState.assertFocusedActivity( 923 "Focus must be on newly launched app", SECOND_ACTIVITY); 924 final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId(); 925 ActivityManagerState.ActivityStack focusedStack = 926 mAmWmState.getAmState().getStackById(externalFocusedStackId); 927 assertEquals("Focused stack must be on secondary display", newDisplay.mId, 928 focusedStack.mDisplayId); 929 930 // Launch another activity with third different uid from app on secondary display and 931 // check it is launched on secondary display. 932 getLaunchActivityBuilder() 933 .setUseBroadcastReceiver(SECOND_LAUNCH_BROADCAST_RECEIVER, 934 SECOND_LAUNCH_BROADCAST_ACTION) 935 .setDisplayId(newDisplay.mId) 936 .setTargetActivity(THIRD_ACTIVITY) 937 .execute(); 938 939 mAmWmState.waitForValidState(THIRD_ACTIVITY); 940 mAmWmState.assertFocusedActivity("Focus must be on newly launched app", THIRD_ACTIVITY); 941 assertEquals("Activity launched by app on secondary display must be on that display", 942 externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId()); 943 } 944 } 945 946 /** Tests that an activity can launch an activity from a different UID into its own task. */ 947 @Test 948 public void testPermissionLaunchMultiUidTask() throws Exception { 949 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 950 final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true) 951 .createDisplay(); 952 953 launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); 954 mAmWmState.computeState(LAUNCHING_ACTIVITY); 955 956 // Check that the first activity is launched onto the secondary display 957 final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 958 ActivityManagerState.ActivityStack frontStack = mAmWmState.getAmState().getStackById( 959 frontStackId); 960 assertEquals("Activity launched on secondary display must be resumed", 961 getActivityName(LAUNCHING_ACTIVITY), 962 frontStack.mResumedActivity); 963 mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId); 964 965 // Launch an activity from a different UID into the first activity's task 966 getLaunchActivityBuilder().setTargetActivity(SECOND_ACTIVITY).execute(); 967 968 mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId); 969 frontStack = mAmWmState.getAmState().getStackById(frontStackId); 970 mAmWmState.assertFocusedActivity( 971 "Focus must be on newly launched app", SECOND_ACTIVITY); 972 assertEquals("Secondary display must contain 1 task", 1, frontStack.getTasks().size()); 973 } 974 } 975 976 /** 977 * Test that launching from display owner is allowed even when the the display owner 978 * doesn't have anything on the display. 979 */ 980 @Test 981 public void testPermissionLaunchFromOwner() throws Exception { 982 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 983 // Create new virtual display. 984 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 985 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 986 mAmWmState.assertFocusedActivity("Virtual display activity must be focused", 987 VIRTUAL_DISPLAY_ACTIVITY); 988 final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId(); 989 ActivityManagerState.ActivityStack focusedStack = 990 mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId); 991 assertEquals("Focus must remain on primary display", 992 DEFAULT_DISPLAY, focusedStack.mDisplayId); 993 994 // Launch other activity with different uid on secondary display. 995 final String startCmd = "am start -n " + getActivityName(SECOND_ACTIVITY) 996 + " --display " + newDisplay.mId; 997 executeShellCommand(startCmd); 998 999 mAmWmState.waitForValidState(SECOND_ACTIVITY); 1000 mAmWmState.assertFocusedActivity( 1001 "Focus must be on newly launched app", SECOND_ACTIVITY); 1002 final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId(); 1003 focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId); 1004 assertEquals("Focused stack must be on secondary display", newDisplay.mId, 1005 focusedStack.mDisplayId); 1006 1007 // Check that owner uid can launch its own activity on secondary display. 1008 getLaunchActivityBuilder() 1009 .setUseBroadcastReceiver(LAUNCH_BROADCAST_RECEIVER, LAUNCH_BROADCAST_ACTION) 1010 .setNewTask(true) 1011 .setMultipleTask(true) 1012 .setDisplayId(newDisplay.mId) 1013 .execute(); 1014 1015 mAmWmState.waitForValidState(TEST_ACTIVITY); 1016 mAmWmState.assertFocusedActivity("Focus must be on newly launched app", 1017 TEST_ACTIVITY); 1018 assertEquals("Activity launched by owner must be on external display", 1019 externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId()); 1020 } 1021 } 1022 1023 /** 1024 * Test that launching from app that is not present on external display and doesn't own it to 1025 * that external display is not allowed. 1026 */ 1027 @Test 1028 public void testPermissionLaunchFromDifferentApp() throws Exception { 1029 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 1030 // Create new virtual display. 1031 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 1032 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 1033 mAmWmState.assertFocusedActivity("Virtual display activity must be focused", 1034 VIRTUAL_DISPLAY_ACTIVITY); 1035 final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId(); 1036 ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById( 1037 defaultDisplayFocusedStackId); 1038 assertEquals("Focus must remain on primary display", 1039 DEFAULT_DISPLAY, focusedStack.mDisplayId); 1040 1041 // Launch activity on new secondary display. 1042 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 1043 mAmWmState.assertFocusedActivity("Focus must be on secondary display", 1044 TEST_ACTIVITY); 1045 final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId(); 1046 focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId); 1047 assertEquals("Focused stack must be on secondary display", newDisplay.mId, 1048 focusedStack.mDisplayId); 1049 1050 final LogSeparator logSeparator = separateLogs(); 1051 1052 // Launch other activity with different uid and check security exception is triggered. 1053 getLaunchActivityBuilder() 1054 .setUseBroadcastReceiver(SECOND_LAUNCH_BROADCAST_RECEIVER, 1055 SECOND_LAUNCH_BROADCAST_ACTION) 1056 .setDisplayId(newDisplay.mId) 1057 .setTargetActivity(THIRD_ACTIVITY) 1058 .execute(); 1059 1060 assertSecurityException("ActivityLauncher", logSeparator); 1061 1062 mAmWmState.waitForValidState(TEST_ACTIVITY); 1063 mAmWmState.assertFocusedActivity("Focus must be on first activity", TEST_ACTIVITY); 1064 assertEquals("Focused stack must be on secondary display's stack", 1065 externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId()); 1066 } 1067 } 1068 1069 private void assertSecurityException(String component, LogSeparator logSeparator) 1070 throws Exception { 1071 final Pattern pattern = Pattern.compile(".*SecurityException launching activity.*"); 1072 for (int retry = 1; retry <= 5; retry++) { 1073 String[] logs = getDeviceLogsForComponents(logSeparator, component); 1074 for (String line : logs) { 1075 Matcher m = pattern.matcher(line); 1076 if (m.matches()) { 1077 return; 1078 } 1079 } 1080 logAlways("***Waiting for SecurityException for " + component + " ... retry=" + retry); 1081 try { 1082 Thread.sleep(500); 1083 } catch (InterruptedException e) { 1084 } 1085 } 1086 fail("Expected exception for " + component + " not found"); 1087 } 1088 1089 /** 1090 * Test that only private virtual display can show content with insecure keyguard. 1091 */ 1092 @Test 1093 public void testFlagShowWithInsecureKeyguardOnPublicVirtualDisplay() throws Exception { 1094 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 1095 // Try to create new show-with-insecure-keyguard public virtual display. 1096 final ActivityDisplay newDisplay = virtualDisplaySession 1097 .setPublicDisplay(true) 1098 .setCanShowWithInsecureKeyguard(true) 1099 .setMustBeCreated(false) 1100 .createDisplay(); 1101 1102 // Check that the display is not created. 1103 assertNull(newDisplay); 1104 } 1105 } 1106 1107 /** 1108 * Test that all activities that were on the private display are destroyed on display removal. 1109 */ 1110 @Test 1111 public void testContentDestroyOnDisplayRemoved() throws Exception { 1112 LogSeparator logSeparator; 1113 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 1114 // Create new private virtual display. 1115 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 1116 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 1117 1118 // Launch activities on new secondary display. 1119 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 1120 mAmWmState.assertVisibility(TEST_ACTIVITY, true /* visible */); 1121 mAmWmState.assertFocusedActivity("Launched activity must be focused", 1122 TEST_ACTIVITY); 1123 launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId); 1124 mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */); 1125 mAmWmState.assertFocusedActivity("Launched activity must be focused", 1126 RESIZEABLE_ACTIVITY); 1127 1128 // Destroy the display and check if activities are removed from system. 1129 logSeparator = separateLogs(); 1130 } 1131 1132 mAmWmState.waitForWithAmState( 1133 (state) -> !state.containsActivity(TEST_ACTIVITY) 1134 && !state.containsActivity(RESIZEABLE_ACTIVITY), 1135 "Waiting for activity to be removed"); 1136 mAmWmState.waitForWithWmState( 1137 (state) -> !state.containsWindow(getWindowName(TEST_ACTIVITY)) 1138 && !state.containsWindow(getWindowName(RESIZEABLE_ACTIVITY)), 1139 "Waiting for activity window to be gone"); 1140 1141 // Check AM state. 1142 assertFalse("Activity from removed display must be destroyed", 1143 mAmWmState.getAmState().containsActivity(TEST_ACTIVITY)); 1144 assertFalse("Activity from removed display must be destroyed", 1145 mAmWmState.getAmState().containsActivity(RESIZEABLE_ACTIVITY)); 1146 // Check WM state. 1147 assertFalse("Activity windows from removed display must be destroyed", 1148 mAmWmState.getWmState().containsWindow(getWindowName(TEST_ACTIVITY))); 1149 assertFalse("Activity windows from removed display must be destroyed", 1150 mAmWmState.getWmState().containsWindow(getWindowName(RESIZEABLE_ACTIVITY))); 1151 // Check activity logs. 1152 assertActivityDestroyed(TEST_ACTIVITY, logSeparator); 1153 assertActivityDestroyed(RESIZEABLE_ACTIVITY, logSeparator); 1154 } 1155 1156 /** 1157 * Test that the update of display metrics updates all its content. 1158 */ 1159 @Test 1160 public void testDisplayResize() throws Exception { 1161 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 1162 // Create new virtual display. 1163 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 1164 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 1165 1166 // Launch a resizeable activity on new secondary display. 1167 final LogSeparator initialLogSeparator = separateLogs(); 1168 launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId); 1169 mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */); 1170 mAmWmState.assertFocusedActivity("Launched activity must be focused", 1171 RESIZEABLE_ACTIVITY); 1172 1173 // Grab reported sizes and compute new with slight size change. 1174 final ReportedSizes initialSize = getLastReportedSizesForActivity( 1175 RESIZEABLE_ACTIVITY, initialLogSeparator); 1176 1177 // Resize the display 1178 final LogSeparator logSeparator = separateLogs(); 1179 virtualDisplaySession.resizeDisplay(); 1180 1181 mAmWmState.waitForWithAmState(amState -> { 1182 try { 1183 return readConfigChangeNumber(RESIZEABLE_ACTIVITY, logSeparator) == 1 1184 && amState.hasActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED); 1185 } catch (Exception e) { 1186 logE("Error waiting for valid state: " + e.getMessage()); 1187 return false; 1188 } 1189 }, "Wait for the configuration change to happen and for activity to be resumed."); 1190 1191 mAmWmState.computeState(false /* compareTaskAndStackBounds */, 1192 new WaitForValidActivityState(RESIZEABLE_ACTIVITY), 1193 new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY)); 1194 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true); 1195 mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true); 1196 1197 // Check if activity in virtual display was resized properly. 1198 assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */, 1199 1 /* numConfigChange */, logSeparator); 1200 1201 final ReportedSizes updatedSize = getLastReportedSizesForActivity( 1202 RESIZEABLE_ACTIVITY, logSeparator); 1203 assertTrue(updatedSize.widthDp <= initialSize.widthDp); 1204 assertTrue(updatedSize.heightDp <= initialSize.heightDp); 1205 assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2); 1206 assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2); 1207 } 1208 } 1209 1210 /** Read the number of configuration changes sent to activity from logs. */ 1211 private int readConfigChangeNumber(ComponentName activityName, LogSeparator logSeparator) 1212 throws Exception { 1213 return (new ActivityLifecycleCounts(activityName, logSeparator)).mConfigurationChangedCount; 1214 } 1215 1216 /** 1217 * Tests that when an activity is launched with displayId specified and there is an existing 1218 * matching task on some other display - that task will moved to the target display. 1219 */ 1220 @Test 1221 public void testMoveToDisplayOnLaunch() throws Exception { 1222 // Launch activity with unique affinity, so it will the only one in its task. 1223 launchActivity(LAUNCHING_ACTIVITY); 1224 1225 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 1226 // Create new virtual display. 1227 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 1228 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 1229 // Launch something to that display so that a new stack is created. We need this to be 1230 // able to compare task numbers in stacks later. 1231 launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId); 1232 mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */); 1233 1234 final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY) 1235 .mStacks.size(); 1236 final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 1237 final int taskNumOnSecondary = mAmWmState.getAmState().getStackById(frontStackId) 1238 .getTasks().size(); 1239 1240 // Launch activity on new secondary display. 1241 // Using custom command here, because normally we add flags 1242 // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 1243 // when launching on some specific display. We don't do it here as we want an existing 1244 // task to be used. 1245 final String launchCommand = "am start -n " + getActivityName(LAUNCHING_ACTIVITY) 1246 + " --display " + newDisplay.mId; 1247 executeShellCommand(launchCommand); 1248 mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED); 1249 1250 // Check that activity is brought to front. 1251 mAmWmState.assertFocusedActivity("Existing task must be brought to front", 1252 LAUNCHING_ACTIVITY); 1253 mAmWmState.assertResumedActivity("Existing task must be resumed", 1254 LAUNCHING_ACTIVITY); 1255 1256 // Check that activity is on the right display. 1257 final ActivityManagerState.ActivityStack firstFrontStack = 1258 mAmWmState.getAmState().getStackById(frontStackId); 1259 assertEquals("Activity must be moved to the secondary display", 1260 getActivityName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity); 1261 mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId); 1262 1263 // Check that task has moved from primary display to secondary. 1264 final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY) 1265 .mStacks.size(); 1266 assertEquals("Stack number in default stack must be decremented.", stackNum - 1, 1267 stackNumFinal); 1268 final int taskNumFinalOnSecondary = mAmWmState.getAmState().getStackById(frontStackId) 1269 .getTasks().size(); 1270 assertEquals("Task number in stack on external display must be incremented.", 1271 taskNumOnSecondary + 1, taskNumFinalOnSecondary); 1272 } 1273 } 1274 1275 /** 1276 * Tests that when an activity is launched with displayId specified and there is an existing 1277 * matching task on some other display - that task will moved to the target display. 1278 */ 1279 @Test 1280 public void testMoveToEmptyDisplayOnLaunch() throws Exception { 1281 // Launch activity with unique affinity, so it will the only one in its task. 1282 launchActivity(LAUNCHING_ACTIVITY); 1283 1284 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 1285 // Create new virtual display. 1286 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 1287 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 1288 1289 final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY).mStacks.size(); 1290 1291 // Launch activity on new secondary display. 1292 // Using custom command here, because normally we add flags 1293 // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 1294 // when launching on some specific display. We don't do it here as we want an existing 1295 // task to be used. 1296 final String launchCommand = "am start -n " + getActivityName(LAUNCHING_ACTIVITY) 1297 + " --display " + newDisplay.mId; 1298 executeShellCommand(launchCommand); 1299 mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED); 1300 1301 // Check that activity is brought to front. 1302 mAmWmState.assertFocusedActivity("Existing task must be brought to front", 1303 LAUNCHING_ACTIVITY); 1304 mAmWmState.assertResumedActivity("Existing task must be resumed", 1305 LAUNCHING_ACTIVITY); 1306 1307 // Check that activity is on the right display. 1308 final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 1309 final ActivityManagerState.ActivityStack firstFrontStack = 1310 mAmWmState.getAmState().getStackById(frontStackId); 1311 assertEquals("Activity must be moved to the secondary display", 1312 getActivityName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity); 1313 mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId); 1314 1315 // Check that task has moved from primary display to secondary. 1316 final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY) 1317 .mStacks.size(); 1318 assertEquals("Stack number in default stack must be decremented.", stackNum - 1, 1319 stackNumFinal); 1320 } 1321 } 1322 1323 /** 1324 * Tests that when primary display is rotated secondary displays are not affected. 1325 */ 1326 @Test 1327 public void testRotationNotAffectingSecondaryScreen() throws Exception { 1328 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 1329 // Create new virtual display. 1330 final ActivityDisplay newDisplay = virtualDisplaySession.setResizeDisplay(false) 1331 .createDisplay(); 1332 mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 1333 1334 // Launch activity on new secondary display. 1335 LogSeparator logSeparator = separateLogs(); 1336 launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId); 1337 mAmWmState.assertFocusedActivity("Focus must be on secondary display", 1338 RESIZEABLE_ACTIVITY); 1339 final ReportedSizes initialSizes = getLastReportedSizesForActivity( 1340 RESIZEABLE_ACTIVITY, logSeparator); 1341 assertNotNull("Test activity must have reported initial sizes on launch", initialSizes); 1342 1343 try (final RotationSession rotationSession = new RotationSession()) { 1344 // Rotate primary display and check that activity on secondary display is not 1345 // affected. 1346 1347 rotateAndCheckSameSizes(rotationSession, RESIZEABLE_ACTIVITY); 1348 1349 // Launch activity to secondary display when primary one is rotated. 1350 final int initialRotation = mAmWmState.getWmState().getRotation(); 1351 rotationSession.set((initialRotation + 1) % 4); 1352 1353 logSeparator = separateLogs(); 1354 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 1355 mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED); 1356 mAmWmState.assertFocusedActivity("Focus must be on secondary display", 1357 TEST_ACTIVITY); 1358 final ReportedSizes testActivitySizes = getLastReportedSizesForActivity( 1359 TEST_ACTIVITY, logSeparator); 1360 assertEquals( 1361 "Sizes of secondary display must not change after rotation of primary " 1362 + "display", 1363 initialSizes, testActivitySizes); 1364 } 1365 } 1366 } 1367 1368 private void rotateAndCheckSameSizes( 1369 RotationSession rotationSession, ComponentName activityName) throws Exception { 1370 for (int rotation = 3; rotation >= 0; --rotation) { 1371 final LogSeparator logSeparator = separateLogs(); 1372 rotationSession.set(rotation); 1373 final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName, 1374 logSeparator); 1375 assertNull("Sizes must not change after rotation", rotatedSizes); 1376 } 1377 } 1378 1379 /** 1380 * Tests that task affinity does affect what display an activity is launched on but that 1381 * matching the task component root does. 1382 */ 1383 @Test 1384 public void testTaskMatchAcrossDisplays() throws Exception { 1385 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 1386 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 1387 1388 launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); 1389 mAmWmState.computeState(LAUNCHING_ACTIVITY); 1390 1391 // Check that activity is on the secondary display. 1392 final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 1393 final ActivityManagerState.ActivityStack firstFrontStack = 1394 mAmWmState.getAmState().getStackById(frontStackId); 1395 assertEquals("Activity launched on secondary display must be resumed", 1396 getActivityName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity); 1397 mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId); 1398 1399 executeShellCommand("am start -n " + getActivityName(ALT_LAUNCHING_ACTIVITY)); 1400 mAmWmState.waitForValidState(ALT_LAUNCHING_ACTIVITY); 1401 1402 // Check that second activity gets launched on the default display despite 1403 // the affinity match on the secondary display. 1404 final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId( 1405 DEFAULT_DISPLAY); 1406 final ActivityManagerState.ActivityStack defaultDisplayFrontStack = 1407 mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId); 1408 assertEquals("Activity launched on default display must be resumed", 1409 getActivityName(ALT_LAUNCHING_ACTIVITY), 1410 defaultDisplayFrontStack.mResumedActivity); 1411 mAmWmState.assertFocusedStack("Focus must be on primary display", 1412 defaultDisplayFrontStackId); 1413 1414 executeShellCommand("am start -n " + getActivityName(LAUNCHING_ACTIVITY)); 1415 mAmWmState.waitForFocusedStack(frontStackId); 1416 1417 // Check that the third intent is redirected to the first task due to the root 1418 // component match on the secondary display. 1419 final ActivityManagerState.ActivityStack secondFrontStack = 1420 mAmWmState.getAmState().getStackById(frontStackId); 1421 assertEquals("Activity launched on secondary display must be resumed", 1422 getActivityName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity); 1423 mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId); 1424 assertEquals("Focused stack must only contain 1 task", 1425 1, secondFrontStack.getTasks().size()); 1426 assertEquals("Focused task must only contain 1 activity", 1427 1, secondFrontStack.getTasks().get(0).mActivities.size()); 1428 } 1429 } 1430 1431 /** 1432 * Tests that the task affinity search respects the launch display id. 1433 */ 1434 @Test 1435 public void testLaunchDisplayAffinityMatch() throws Exception { 1436 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 1437 final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay(); 1438 1439 launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); 1440 1441 // Check that activity is on the secondary display. 1442 final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 1443 final ActivityManagerState.ActivityStack firstFrontStack = 1444 mAmWmState.getAmState().getStackById(frontStackId); 1445 assertEquals("Activity launched on secondary display must be resumed", 1446 getActivityName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity); 1447 mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId); 1448 1449 // We don't want FLAG_ACTIVITY_MULTIPLE_TASK, so we can't use launchActivityOnDisplay 1450 executeShellCommand("am start -n " + getActivityName(ALT_LAUNCHING_ACTIVITY) 1451 + " -f 0x10000000" // FLAG_ACTIVITY_NEW_TASK 1452 + " --display " + newDisplay.mId); 1453 mAmWmState.computeState(ALT_LAUNCHING_ACTIVITY); 1454 1455 // Check that second activity gets launched into the affinity matching 1456 // task on the secondary display 1457 final int secondFrontStackId = 1458 mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 1459 final ActivityManagerState.ActivityStack secondFrontStack = 1460 mAmWmState.getAmState().getStackById(secondFrontStackId); 1461 assertEquals("Activity launched on secondary display must be resumed", 1462 getActivityName(ALT_LAUNCHING_ACTIVITY), 1463 secondFrontStack.mResumedActivity); 1464 mAmWmState.assertFocusedStack("Focus must be on secondary display", 1465 secondFrontStackId); 1466 assertEquals("Focused stack must only contain 1 task", 1467 1, secondFrontStack.getTasks().size()); 1468 assertEquals("Focused task must contain 2 activities", 1469 2, secondFrontStack.getTasks().get(0).mActivities.size()); 1470 } 1471 } 1472 1473 /** 1474 * Tests than a new task launched by an activity will end up on that activity's display 1475 * even if the focused stack is not on that activity's display. 1476 */ 1477 @Test 1478 public void testNewTaskSameDisplay() throws Exception { 1479 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 1480 final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true) 1481 .createDisplay(); 1482 1483 launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId); 1484 mAmWmState.computeState(BROADCAST_RECEIVER_ACTIVITY); 1485 1486 // Check that the first activity is launched onto the secondary display 1487 final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 1488 final ActivityManagerState.ActivityStack firstFrontStack = 1489 mAmWmState.getAmState().getStackById(frontStackId); 1490 assertEquals("Activity launched on secondary display must be resumed", 1491 getActivityName(BROADCAST_RECEIVER_ACTIVITY), 1492 firstFrontStack.mResumedActivity); 1493 mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId); 1494 1495 executeShellCommand("am start -n " + getActivityName(TEST_ACTIVITY)); 1496 mAmWmState.waitForValidState(TEST_ACTIVITY); 1497 1498 // Check that the second activity is launched on the default display 1499 final int focusedStackId = mAmWmState.getAmState().getFocusedStackId(); 1500 final ActivityManagerState.ActivityStack focusedStack = 1501 mAmWmState.getAmState().getStackById(focusedStackId); 1502 assertEquals("Activity launched on default display must be resumed", 1503 getActivityName(TEST_ACTIVITY), focusedStack.mResumedActivity); 1504 assertEquals("Focus must be on primary display", 1505 DEFAULT_DISPLAY, focusedStack.mDisplayId); 1506 1507 executeShellCommand(LAUNCH_ACTIVITY_BROADCAST + getActivityName(LAUNCHING_ACTIVITY)); 1508 1509 // Check that the third activity ends up in a new task in the same stack as the 1510 // first activity 1511 mAmWmState.waitForValidState(LAUNCHING_ACTIVITY); 1512 mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId); 1513 final ActivityManagerState.ActivityStack secondFrontStack = 1514 mAmWmState.getAmState().getStackById(frontStackId); 1515 assertEquals("Activity must be launched on secondary display", 1516 getActivityName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity); 1517 assertEquals("Secondary display must contain 2 tasks", 1518 2, secondFrontStack.getTasks().size()); 1519 } 1520 } 1521 1522 /** 1523 * Tests than an immediate launch after new display creation is handled correctly. 1524 */ 1525 @Test 1526 public void testImmediateLaunchOnNewDisplay() throws Exception { 1527 try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) { 1528 // Create new virtual display and immediately launch an activity on it. 1529 final ActivityDisplay newDisplay = virtualDisplaySession 1530 .setLaunchActivity(TEST_ACTIVITY) 1531 .createDisplay(); 1532 1533 // Check that activity is launched and placed correctly. 1534 mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED); 1535 mAmWmState.assertResumedActivity("Test activity must be launched on a new display", 1536 TEST_ACTIVITY); 1537 final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId); 1538 final ActivityManagerState.ActivityStack firstFrontStack = 1539 mAmWmState.getAmState().getStackById(frontStackId); 1540 assertEquals("Activity launched on secondary display must be resumed", 1541 getActivityName(TEST_ACTIVITY), firstFrontStack.mResumedActivity); 1542 mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId); 1543 } 1544 } 1545 1546 /** 1547 * Tests that turning the primary display off does not affect the activity running 1548 * on an external secondary display. 1549 */ 1550 @Test 1551 public void testExternalDisplayActivityTurnPrimaryOff() throws Exception { 1552 // Launch something on the primary display so we know there is a resumed activity there 1553 launchActivity(RESIZEABLE_ACTIVITY); 1554 waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY, 1555 "Activity launched on primary display must be resumed"); 1556 1557 try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession(); 1558 final PrimaryDisplayStateSession displayStateSession = 1559 new PrimaryDisplayStateSession()) { 1560 final ActivityDisplay newDisplay = 1561 externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */); 1562 1563 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 1564 1565 // Check that the activity is launched onto the external display 1566 waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId, 1567 "Activity launched on external display must be resumed"); 1568 1569 final LogSeparator logSeparator = separateLogs(); 1570 1571 displayStateSession.turnScreenOff(); 1572 1573 // Wait for the fullscreen stack to start sleeping, and then make sure the 1574 // test activity is still resumed. 1575 int retry = 0; 1576 ActivityLifecycleCounts lifecycleCounts; 1577 do { 1578 lifecycleCounts = new ActivityLifecycleCounts(RESIZEABLE_ACTIVITY, logSeparator); 1579 if (lifecycleCounts.mStopCount == 1) { 1580 break; 1581 } 1582 logAlways("***testExternalDisplayActivityTurnPrimaryOff... retry=" + retry); 1583 SystemClock.sleep(TimeUnit.SECONDS.toMillis(1)); 1584 } while (retry++ < 5); 1585 1586 if (lifecycleCounts.mStopCount != 1) { 1587 fail(RESIZEABLE_ACTIVITY + " has received " + lifecycleCounts.mStopCount 1588 + " onStop() calls, expecting 1"); 1589 } 1590 waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId, 1591 "Activity launched on external display must be resumed"); 1592 } 1593 } 1594 1595 /** 1596 * Tests that an activity can be launched on a secondary display while the primary 1597 * display is off. 1598 */ 1599 @Test 1600 public void testLaunchExternalDisplayActivityWhilePrimaryOff() throws Exception { 1601 // Launch something on the primary display so we know there is a resumed activity there 1602 launchActivity(RESIZEABLE_ACTIVITY); 1603 waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY, 1604 "Activity launched on primary display must be resumed"); 1605 1606 try (final PrimaryDisplayStateSession displayStateSession = 1607 new PrimaryDisplayStateSession(); 1608 final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) { 1609 displayStateSession.turnScreenOff(); 1610 1611 // Make sure there is no resumed activity when the primary display is off 1612 waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY, 1613 "Activity launched on primary display must be stopped after turning off"); 1614 assertEquals("Unexpected resumed activity", 1615 0, mAmWmState.getAmState().getResumedActivitiesCount()); 1616 1617 final ActivityDisplay newDisplay = 1618 externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */); 1619 1620 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 1621 1622 // Check that the test activity is resumed on the external display 1623 waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId, 1624 "Activity launched on external display must be resumed"); 1625 } 1626 } 1627 1628 /** 1629 * Tests that turning the secondary display off stops activities running on that display. 1630 */ 1631 @Test 1632 public void testExternalDisplayToggleState() throws Exception { 1633 try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) { 1634 final ActivityDisplay newDisplay = 1635 externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */); 1636 1637 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 1638 1639 // Check that the test activity is resumed on the external display 1640 waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId, 1641 "Activity launched on external display must be resumed"); 1642 1643 externalDisplaySession.turnDisplayOff(); 1644 1645 // Check that turning off the external display stops the activity 1646 waitAndAssertActivityStopped(TEST_ACTIVITY, 1647 "Activity launched on external display must be stopped after turning off"); 1648 1649 externalDisplaySession.turnDisplayOn(); 1650 1651 // Check that turning on the external display resumes the activity 1652 waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId, 1653 "Activity launched on external display must be resumed"); 1654 } 1655 } 1656 1657 /** 1658 * Tests that tapping on the primary display after showing the keyguard resumes the 1659 * activity on the primary display. 1660 */ 1661 @Test 1662 public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception { 1663 // Launch something on the primary display so we know there is a resumed activity there 1664 launchActivity(RESIZEABLE_ACTIVITY); 1665 waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY, 1666 "Activity launched on primary display must be resumed"); 1667 1668 try (final LockScreenSession lockScreenSession = new LockScreenSession(); 1669 final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) { 1670 lockScreenSession.sleepDevice(); 1671 1672 // Make sure there is no resumed activity when the primary display is off 1673 waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY, 1674 "Activity launched on primary display must be stopped after turning off"); 1675 assertEquals("Unexpected resumed activity", 1676 0, mAmWmState.getAmState().getResumedActivitiesCount()); 1677 1678 final ActivityDisplay newDisplay = 1679 externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */); 1680 1681 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); 1682 1683 // Check that the test activity is resumed on the external display 1684 waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId, 1685 "Activity launched on external display must be resumed"); 1686 1687 // Unlock the device and tap on the middle of the primary display 1688 lockScreenSession.wakeUpDevice(); 1689 executeShellCommand("wm dismiss-keyguard"); 1690 mAmWmState.waitForKeyguardGone(); 1691 mAmWmState.waitForValidState(TEST_ACTIVITY); 1692 final ReportedDisplayMetrics displayMetrics = getDisplayMetrics(); 1693 final int width = displayMetrics.getSize().getWidth(); 1694 final int height = displayMetrics.getSize().getHeight(); 1695 executeShellCommand("input tap " + (width / 2) + " " + (height / 2)); 1696 1697 // Check that the activity on the primary display is resumed 1698 waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY, 1699 "Activity launched on primary display must be resumed"); 1700 assertEquals("Unexpected resumed activity", 1701 1, mAmWmState.getAmState().getResumedActivitiesCount()); 1702 } 1703 } 1704 1705 private void waitAndAssertActivityResumed( 1706 ComponentName activityName, int displayId, String message) throws Exception { 1707 mAmWmState.waitForActivityState(activityName, STATE_RESUMED); 1708 1709 assertEquals(message, 1710 getActivityName(activityName), mAmWmState.getAmState().getResumedActivity()); 1711 final int frontStackId = mAmWmState.getAmState().getFrontStackId(displayId); 1712 ActivityManagerState.ActivityStack firstFrontStack = 1713 mAmWmState.getAmState().getStackById(frontStackId); 1714 assertEquals(message, 1715 getActivityName(activityName), firstFrontStack.mResumedActivity); 1716 assertTrue(message, 1717 mAmWmState.getAmState().hasActivityState(activityName, STATE_RESUMED)); 1718 mAmWmState.assertFocusedStack("Focus must be on external display", frontStackId); 1719 mAmWmState.assertVisibility(activityName, true /* visible */); 1720 } 1721 1722 private void waitAndAssertActivityStopped(ComponentName activityName, String message) 1723 throws Exception { 1724 mAmWmState.waitForActivityState(activityName, STATE_STOPPED); 1725 1726 assertTrue(message, mAmWmState.getAmState().hasActivityState(activityName, 1727 STATE_STOPPED)); 1728 } 1729 1730 /** 1731 * Tests that showWhenLocked works on a secondary display. 1732 */ 1733 @Test 1734 public void testSecondaryDisplayShowWhenLocked() throws Exception { 1735 try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession(); 1736 final LockScreenSession lockScreenSession = new LockScreenSession()) { 1737 lockScreenSession.setLockCredential(); 1738 1739 launchActivity(TEST_ACTIVITY); 1740 1741 final ActivityDisplay newDisplay = 1742 externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */); 1743 launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, newDisplay.mId); 1744 1745 lockScreenSession.gotoKeyguard(); 1746 1747 mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_STOPPED); 1748 mAmWmState.waitForActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, STATE_RESUMED); 1749 1750 mAmWmState.computeState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY); 1751 assertTrue("Expected resumed activity on secondary display", mAmWmState.getAmState() 1752 .hasActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, STATE_RESUMED)); 1753 } 1754 } 1755 1756 /** Assert that component received onMovedToDisplay and onConfigurationChanged callbacks. */ 1757 private void assertMovedToDisplay(ComponentName componentName, LogSeparator logSeparator) 1758 throws Exception { 1759 final ActivityLifecycleCounts lifecycleCounts = 1760 new ActivityLifecycleCounts(componentName, logSeparator); 1761 if (lifecycleCounts.mDestroyCount != 0) { 1762 fail(componentName + " has been destroyed " + lifecycleCounts.mDestroyCount 1763 + " time(s), wasn't expecting any"); 1764 } else if (lifecycleCounts.mCreateCount != 0) { 1765 fail(componentName + " has been (re)created " + lifecycleCounts.mCreateCount 1766 + " time(s), wasn't expecting any"); 1767 } else if (lifecycleCounts.mConfigurationChangedCount != 1) { 1768 fail(componentName + " has received " 1769 + lifecycleCounts.mConfigurationChangedCount 1770 + " onConfigurationChanged() calls, expecting " + 1); 1771 } else if (lifecycleCounts.mMovedToDisplayCount != 1) { 1772 fail(componentName + " has received " 1773 + lifecycleCounts.mMovedToDisplayCount 1774 + " onMovedToDisplay() calls, expecting " + 1); 1775 } 1776 } 1777 1778 private class ExternalDisplaySession implements AutoCloseable { 1779 1780 @Nullable 1781 private VirtualDisplayHelper mExternalDisplayHelper; 1782 1783 /** 1784 * Creates a private virtual display with the external and show with insecure 1785 * keyguard flags set. 1786 */ 1787 ActivityDisplay createVirtualDisplay(boolean showContentWhenLocked) 1788 throws Exception { 1789 final List<ActivityDisplay> originalDS = getDisplaysStates(); 1790 final int originalDisplayCount = originalDS.size(); 1791 1792 mExternalDisplayHelper = new VirtualDisplayHelper(); 1793 mExternalDisplayHelper.createAndWaitForDisplay(showContentWhenLocked); 1794 1795 // Wait for the virtual display to be created and get configurations. 1796 final List<ActivityDisplay> ds = getDisplayStateAfterChange(originalDisplayCount + 1); 1797 assertEquals("New virtual display must be created", originalDisplayCount + 1, 1798 ds.size()); 1799 1800 // Find the newly added display. 1801 final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds); 1802 return newDisplays.get(0); 1803 } 1804 1805 void turnDisplayOff() { 1806 if (mExternalDisplayHelper == null) { 1807 throw new RuntimeException("No external display created"); 1808 } 1809 mExternalDisplayHelper.turnDisplayOff(); 1810 } 1811 1812 void turnDisplayOn() { 1813 if (mExternalDisplayHelper == null) { 1814 throw new RuntimeException("No external display created"); 1815 } 1816 mExternalDisplayHelper.turnDisplayOn(); 1817 } 1818 1819 @Override 1820 public void close() throws Exception { 1821 if (mExternalDisplayHelper != null) { 1822 mExternalDisplayHelper.releaseDisplay(); 1823 mExternalDisplayHelper = null; 1824 } 1825 } 1826 } 1827 1828 private static class PrimaryDisplayStateSession implements AutoCloseable { 1829 1830 void turnScreenOff() { 1831 setPrimaryDisplayState(false); 1832 } 1833 1834 @Override 1835 public void close() throws Exception { 1836 setPrimaryDisplayState(true); 1837 } 1838 1839 /** Turns the primary display on/off by pressing the power key */ 1840 private void setPrimaryDisplayState(boolean wantOn) { 1841 if (wantOn) { 1842 pressWakeupButton(); 1843 } else { 1844 pressSleepButton(); 1845 } 1846 VirtualDisplayHelper.waitForDefaultDisplayState(wantOn); 1847 } 1848 } 1849} 1850