1 /* 2 * Copyright (C) 2015 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.ActivityManager.LOCK_TASK_MODE_NONE; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 23 import static android.server.wm.TestTaskOrganizer.INVALID_TASK_ID; 24 import static android.server.wm.WindowManagerState.STATE_RESUMED; 25 import static android.server.wm.WindowManagerState.STATE_STOPPED; 26 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY; 27 import static android.server.wm.app.Components.NON_RESIZEABLE_ACTIVITY; 28 import static android.server.wm.app.Components.NO_RELAUNCH_ACTIVITY; 29 import static android.server.wm.app.Components.SINGLE_INSTANCE_ACTIVITY; 30 import static android.server.wm.app.Components.SINGLE_TASK_ACTIVITY; 31 import static android.server.wm.app.Components.TEST_ACTIVITY; 32 import static android.server.wm.app.Components.TEST_ACTIVITY_WITH_SAME_AFFINITY; 33 import static android.server.wm.app.Components.TRANSLUCENT_TEST_ACTIVITY; 34 import static android.server.wm.app.Components.TestActivity.TEST_ACTIVITY_ACTION_FINISH_SELF; 35 import static android.server.wm.app27.Components.SDK_27_LAUNCHING_ACTIVITY; 36 import static android.server.wm.app27.Components.SDK_27_SEPARATE_PROCESS_ACTIVITY; 37 import static android.server.wm.app27.Components.SDK_27_TEST_ACTIVITY; 38 39 import static org.junit.Assert.assertEquals; 40 import static org.junit.Assert.assertTrue; 41 import static org.junit.Assert.fail; 42 import static org.junit.Assume.assumeTrue; 43 44 import android.content.ComponentName; 45 import android.content.res.Resources; 46 import android.platform.test.annotations.Presubmit; 47 import android.server.wm.CommandSession.ActivityCallback; 48 import android.window.WindowContainerToken; 49 import android.window.WindowContainerTransaction; 50 51 import org.junit.Before; 52 import org.junit.Test; 53 54 /** 55 * Build/Install/Run: 56 * atest CtsWindowManagerDeviceTestCases:MultiWindowTests 57 */ 58 @Presubmit 59 @android.server.wm.annotation.Group2 60 public class MultiWindowTests extends ActivityManagerTestBase { 61 62 private boolean mIsHomeRecentsComponent; 63 64 @Before 65 @Override setUp()66 public void setUp() throws Exception { 67 super.setUp(); 68 69 mIsHomeRecentsComponent = mWmState.isHomeRecentsComponent(); 70 71 assumeTrue("Skipping test: no split multi-window support", 72 supportsSplitScreenMultiWindow()); 73 } 74 75 @Test testMinimumDeviceSize()76 public void testMinimumDeviceSize() { 77 mWmState.assertDeviceDefaultDisplaySizeForMultiWindow( 78 "Devices supporting multi-window must be larger than the default minimum" 79 + " task size"); 80 mWmState.assertDeviceDefaultDisplaySizeForSplitScreen( 81 "Devices supporting split-screen multi-window must be larger than the" 82 + " default minimum display size."); 83 } 84 85 /** Resizeable activity should be able to enter multi-window mode.*/ 86 @Test testResizeableActivity()87 public void testResizeableActivity() { 88 assertActivitySupportedInSplitScreen(TEST_ACTIVITY); 89 } 90 91 /** 92 * Depending on the value of 93 * {@link com.android.internal.R.integer.config_supportsNonResizableMultiWindow}, 94 * non-resizeable activity may or may not be able to enter multi-window mode. 95 * 96 * Based on the flag value: 97 * -1: not support non-resizable in multi window. 98 * 0: check the screen smallest width, if it is a large screen, support non-resizable in multi 99 * window. Otherwise, not support. 100 * 1: always support non-resizable in multi window. 101 */ 102 @Test testNonResizeableActivity()103 public void testNonResizeableActivity() { 104 createManagedDevEnableNonResizableMultiWindowSession().set(0); 105 final Resources resources = mContext.getResources(); 106 final int configSupportsNonResizableMultiWindow; 107 try { 108 configSupportsNonResizableMultiWindow = resources.getInteger(resources.getIdentifier( 109 "config_supportsNonResizableMultiWindow", "integer", "android")); 110 } catch (Resources.NotFoundException e) { 111 fail("Device must define config_supportsNonResizableMultiWindow"); 112 return; 113 } 114 switch (configSupportsNonResizableMultiWindow) { 115 case -1: 116 assertActivityNotSupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY); 117 break; 118 case 1: 119 assertActivitySupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY); 120 break; 121 case 0: 122 final int configLargeScreenSmallestScreenWidthDp; 123 try { 124 configLargeScreenSmallestScreenWidthDp = 125 resources.getInteger(resources.getIdentifier( 126 "config_largeScreenSmallestScreenWidthDp", 127 "integer", "android")); 128 } catch (Resources.NotFoundException e) { 129 fail("Device must define config_largeScreenSmallestScreenWidthDp"); 130 return; 131 } 132 final int smallestScreenWidthDp = mWmState.getHomeTask() 133 .mFullConfiguration.smallestScreenWidthDp; 134 if (smallestScreenWidthDp >= configLargeScreenSmallestScreenWidthDp) { 135 assertActivitySupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY); 136 } else { 137 assertActivityNotSupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY); 138 } 139 break; 140 default: 141 fail("config_supportsNonResizableMultiWindow must be -1, 0, or 1."); 142 } 143 } 144 145 /** 146 * Non-resizeable activity can enter split-screen if 147 * {@link android.provider.Settings.Global#DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW} is 148 * set. 149 */ 150 @Test testDevEnableNonResizeableMultiWindow_splitScreenPrimary()151 public void testDevEnableNonResizeableMultiWindow_splitScreenPrimary() { 152 createManagedDevEnableNonResizableMultiWindowSession().set(1); 153 154 assertActivitySupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY); 155 } 156 157 /** 158 * Non-resizeable activity can enter split-screen if 159 * {@link android.provider.Settings.Global#DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW} is 160 * set. 161 */ 162 @Test testDevEnableNonResizeableMultiWindow_splitScreenSecondary()163 public void testDevEnableNonResizeableMultiWindow_splitScreenSecondary() { 164 createManagedDevEnableNonResizableMultiWindowSession().set(1); 165 166 launchActivitiesInSplitScreen( 167 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY), 168 getLaunchActivityBuilder().setTargetActivity(NON_RESIZEABLE_ACTIVITY)); 169 170 mWmState.waitForActivityState(NON_RESIZEABLE_ACTIVITY, STATE_RESUMED); 171 mWmState.assertVisibility(NON_RESIZEABLE_ACTIVITY, true); 172 assertTrue(mWmState.containsActivityInWindowingMode( 173 NON_RESIZEABLE_ACTIVITY, WINDOWING_MODE_MULTI_WINDOW)); 174 } 175 176 /** Asserts that the give activity can be shown in split screen. */ assertActivitySupportedInSplitScreen(ComponentName activity)177 private void assertActivitySupportedInSplitScreen(ComponentName activity) { 178 launchActivityInPrimarySplit(activity); 179 mWmState.waitForActivityState(activity, STATE_RESUMED); 180 mWmState.assertVisibility(activity, true); 181 assertTrue(mWmState.containsActivityInWindowingMode(activity, WINDOWING_MODE_MULTI_WINDOW)); 182 } 183 184 /** Asserts that the give activity can NOT be shown in split screen. */ assertActivityNotSupportedInSplitScreen(ComponentName activity)185 private void assertActivityNotSupportedInSplitScreen(ComponentName activity) { 186 boolean gotAssertionError = false; 187 try { 188 launchActivityInPrimarySplit(activity); 189 } catch (AssertionError e) { 190 gotAssertionError = true; 191 } 192 assertTrue("Trying to put non-resizeable activity in split should throw error.", 193 gotAssertionError); 194 mWmState.waitForActivityState(activity, STATE_RESUMED); 195 mWmState.assertVisibility(activity, true); 196 assertTrue(mWmState.containsActivityInWindowingMode(activity, WINDOWING_MODE_FULLSCREEN)); 197 } 198 199 @Test testLaunchToSideMultiWindowCallbacks()200 public void testLaunchToSideMultiWindowCallbacks() { 201 // Launch two activities in split-screen mode. 202 launchActivitiesInSplitScreen( 203 getLaunchActivityBuilder().setTargetActivity(NO_RELAUNCH_ACTIVITY), 204 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)); 205 206 int displayWindowingMode = mWmState.getDisplay( 207 mWmState.getDisplayByActivity(TEST_ACTIVITY)).getWindowingMode(); 208 separateTestJournal(); 209 mTaskOrganizer.dismissSplitScreen(); 210 if (displayWindowingMode == WINDOWING_MODE_FULLSCREEN) { 211 // Exit split-screen mode and ensure we only get 1 multi-window mode changed callback. 212 final ActivityLifecycleCounts lifecycleCounts = waitForOnMultiWindowModeChanged( 213 NO_RELAUNCH_ACTIVITY); 214 assertEquals(1, 215 lifecycleCounts.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED)); 216 } else { 217 // Display is not a fullscreen display, so there won't be a multi-window callback. 218 // Instead just verify that windows are not in split-screen anymore. 219 waitForIdle(); 220 mWmState.computeState(); 221 mWmState.assertDoesNotContainStack("Must have exited split-screen", 222 WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); 223 } 224 } 225 226 @Test testNoUserLeaveHintOnMultiWindowModeChanged()227 public void testNoUserLeaveHintOnMultiWindowModeChanged() { 228 launchActivity(NO_RELAUNCH_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 229 230 // Move to primary split. 231 separateTestJournal(); 232 putActivityInPrimarySplit(NO_RELAUNCH_ACTIVITY); 233 234 ActivityLifecycleCounts lifecycleCounts = 235 waitForOnMultiWindowModeChanged(NO_RELAUNCH_ACTIVITY); 236 assertEquals("mMultiWindowModeChangedCount", 237 1, lifecycleCounts.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED)); 238 assertEquals("mUserLeaveHintCount", 239 0, lifecycleCounts.getCount(ActivityCallback.ON_USER_LEAVE_HINT)); 240 241 // Make sure primary split is focused. This way when we dismiss it later fullscreen stack 242 // will come up. 243 launchActivity(LAUNCHING_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 244 putActivityInSecondarySplit(LAUNCHING_ACTIVITY); 245 246 launchActivity(NO_RELAUNCH_ACTIVITY); 247 248 separateTestJournal(); 249 250 // Move activities back to fullscreen screen. 251 // TestTaskOrganizer sets windowing modes of tasks to unspecific when putting them to split 252 // screens so we need to explicitly set their windowing modes back to fullscreen to avoid 253 // inheriting freeform windowing mode from the display on freeform first devices. 254 int noRelaunchTaskId = mWmState.getTaskByActivity(NO_RELAUNCH_ACTIVITY).mTaskId; 255 WindowContainerToken noRelaunchTaskToken = 256 mTaskOrganizer.getTaskInfo(noRelaunchTaskId).getToken(); 257 WindowContainerTransaction t = new WindowContainerTransaction() 258 .setWindowingMode(noRelaunchTaskToken, WINDOWING_MODE_FULLSCREEN); 259 mTaskOrganizer.dismissSplitScreen(t, false /* primaryOnTop */); 260 261 lifecycleCounts = waitForOnMultiWindowModeChanged(NO_RELAUNCH_ACTIVITY); 262 assertEquals("mMultiWindowModeChangedCount", 263 1, lifecycleCounts.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED)); 264 assertEquals("mUserLeaveHintCount", 265 0, lifecycleCounts.getCount(ActivityCallback.ON_USER_LEAVE_HINT)); 266 } 267 268 @Test testLaunchToSideAndBringToFront()269 public void testLaunchToSideAndBringToFront() { 270 launchActivitiesInSplitScreen( 271 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY), 272 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)); 273 274 mWmState.assertFocusedActivity("Launched to side activity must be in front.", 275 TEST_ACTIVITY); 276 277 // Launch another activity to side to cover first one. 278 launchActivityInSecondarySplit(NO_RELAUNCH_ACTIVITY); 279 mWmState.assertFocusedActivity("Launched to side covering activity must be in front.", 280 NO_RELAUNCH_ACTIVITY); 281 282 // Launch activity that was first launched to side. It should be brought to front. 283 launchActivity(TEST_ACTIVITY); 284 mWmState.assertFocusedActivity("Launched to side covering activity must be in front.", 285 TEST_ACTIVITY); 286 } 287 288 @Test testLaunchToSideMultiple()289 public void testLaunchToSideMultiple() { 290 launchActivitiesInSplitScreen( 291 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY), 292 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)); 293 294 final int taskNumberInitial = mTaskOrganizer.getSecondarySplitTaskCount(); 295 296 // Try to launch to side same activity again. 297 launchActivity(TEST_ACTIVITY); 298 mWmState.computeState(TEST_ACTIVITY, LAUNCHING_ACTIVITY); 299 final int taskNumberFinal = mTaskOrganizer.getSecondarySplitTaskCount(); 300 assertEquals("Task number mustn't change.", taskNumberInitial, taskNumberFinal); 301 mWmState.assertFocusedActivity("Launched to side activity must remain in front.", 302 TEST_ACTIVITY); 303 } 304 305 @Test testLaunchToSideSingleInstance()306 public void testLaunchToSideSingleInstance() { 307 launchTargetToSide(SINGLE_INSTANCE_ACTIVITY, false); 308 } 309 310 @Test testLaunchToSideSingleTask()311 public void testLaunchToSideSingleTask() { 312 launchTargetToSide(SINGLE_TASK_ACTIVITY, false); 313 } 314 315 @Test testLaunchToSideMultipleWithDifferentIntent()316 public void testLaunchToSideMultipleWithDifferentIntent() { 317 launchTargetToSide(TEST_ACTIVITY, true); 318 } 319 launchTargetToSide(ComponentName targetActivityName, boolean taskCountMustIncrement)320 private void launchTargetToSide(ComponentName targetActivityName, 321 boolean taskCountMustIncrement) { 322 launchActivityInPrimarySplit(LAUNCHING_ACTIVITY); 323 324 // Launch target to side 325 final LaunchActivityBuilder targetActivityLauncher = getLaunchActivityBuilder() 326 .setTargetActivity(targetActivityName) 327 .setToSide(true) 328 .setRandomData(true) 329 .setMultipleTask(false); 330 targetActivityLauncher.execute(); 331 final int secondaryTaskId = mWmState.getTaskByActivity(targetActivityName).mTaskId; 332 mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId); 333 334 mWmState.computeState(targetActivityName, LAUNCHING_ACTIVITY); 335 final int taskNumberInitial = mTaskOrganizer.getSecondarySplitTaskCount(); 336 337 // Try to launch to side same activity again with different data. 338 targetActivityLauncher.execute(); 339 mWmState.computeState(targetActivityName, LAUNCHING_ACTIVITY); 340 341 final int[] excludeTaskIds = new int[] { secondaryTaskId, INVALID_TASK_ID }; 342 if (taskCountMustIncrement) { 343 mWmState.waitFor("Waiting for new activity to come up.", 344 state -> state.getTaskByActivity(targetActivityName, excludeTaskIds) != null); 345 } 346 WindowManagerState.ActivityTask task = mWmState.getTaskByActivity(targetActivityName, 347 excludeTaskIds); 348 final int secondaryTaskId2; 349 if (task != null) { 350 secondaryTaskId2 = task.mTaskId; 351 mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId2); 352 } else { 353 secondaryTaskId2 = INVALID_TASK_ID; 354 } 355 final int taskNumberSecondLaunch = mTaskOrganizer.getSecondarySplitTaskCount(); 356 357 if (taskCountMustIncrement) { 358 assertEquals("Task number must be incremented.", taskNumberInitial + 1, 359 taskNumberSecondLaunch); 360 } else { 361 assertEquals("Task number must not change.", taskNumberInitial, 362 taskNumberSecondLaunch); 363 } 364 mWmState.waitForFocusedActivity("Wait for launched to side activity to be in front.", 365 targetActivityName); 366 mWmState.assertFocusedActivity("Launched to side activity must be in front.", 367 targetActivityName); 368 369 // Try to launch to side same activity again with different random data. Note that null 370 // cannot be used here, since the first instance of TestActivity is launched with no data 371 // in order to launch into split screen. 372 targetActivityLauncher.execute(); 373 mWmState.computeState(targetActivityName, LAUNCHING_ACTIVITY); 374 375 excludeTaskIds[1] = secondaryTaskId2; 376 if (taskCountMustIncrement) { 377 mWmState.waitFor("Waiting for the second new activity to come up.", 378 state -> state.getTaskByActivity(targetActivityName, excludeTaskIds) != null); 379 } 380 WindowManagerState.ActivityTask taskFinal = 381 mWmState.getTaskByActivity(targetActivityName, excludeTaskIds); 382 if (taskFinal != null) { 383 int secondaryTaskId3 = taskFinal.mTaskId; 384 mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId3); 385 } 386 final int taskNumberFinal = mTaskOrganizer.getSecondarySplitTaskCount(); 387 388 if (taskCountMustIncrement) { 389 assertEquals("Task number must be incremented.", taskNumberSecondLaunch + 1, 390 taskNumberFinal); 391 } else { 392 assertEquals("Task number must not change.", taskNumberSecondLaunch, 393 taskNumberFinal); 394 } 395 mWmState.waitForFocusedActivity("Wait for launched to side activity to be in front.", 396 targetActivityName); 397 mWmState.assertFocusedActivity("Launched to side activity must be in front.", 398 targetActivityName); 399 } 400 401 @Test testLaunchToSideMultipleWithFlag()402 public void testLaunchToSideMultipleWithFlag() { 403 launchActivitiesInSplitScreen( 404 getLaunchActivityBuilder() 405 .setTargetActivity(TEST_ACTIVITY), 406 getLaunchActivityBuilder() 407 // Try to launch to side same activity again, 408 // but with Intent#FLAG_ACTIVITY_MULTIPLE_TASK. 409 .setMultipleTask(true) 410 .setTargetActivity(TEST_ACTIVITY)); 411 assertTrue("Primary split must contain TEST_ACTIVITY", 412 mWmState.getRootTask(mTaskOrganizer.getPrimarySplitTaskId()) 413 .containsActivity(TEST_ACTIVITY) 414 ); 415 416 assertTrue("Secondary split must contain TEST_ACTIVITY", 417 mWmState.getRootTask(mTaskOrganizer.getSecondarySplitTaskId()) 418 .containsActivity(TEST_ACTIVITY) 419 ); 420 mWmState.assertFocusedActivity("Launched to side activity must be in front.", 421 TEST_ACTIVITY); 422 } 423 424 @Test testSameProcessActivityResumedPreQ()425 public void testSameProcessActivityResumedPreQ() { 426 launchActivitiesInSplitScreen( 427 getLaunchActivityBuilder().setTargetActivity(SDK_27_TEST_ACTIVITY), 428 getLaunchActivityBuilder().setTargetActivity(SDK_27_LAUNCHING_ACTIVITY)); 429 430 assertEquals("There must be only one resumed activity in the package.", 1, 431 mWmState.getResumedActivitiesCountInPackage( 432 SDK_27_TEST_ACTIVITY.getPackageName())); 433 } 434 435 @Test testDifferentProcessActivityResumedPreQ()436 public void testDifferentProcessActivityResumedPreQ() { 437 launchActivitiesInSplitScreen( 438 getLaunchActivityBuilder().setTargetActivity(SDK_27_TEST_ACTIVITY), 439 getLaunchActivityBuilder().setTargetActivity(SDK_27_SEPARATE_PROCESS_ACTIVITY)); 440 441 assertEquals("There must be only two resumed activities in the package.", 2, 442 mWmState.getResumedActivitiesCountInPackage( 443 SDK_27_TEST_ACTIVITY.getPackageName())); 444 } 445 446 @Test testDisallowUpdateWindowingModeWhenInLockedTask()447 public void testDisallowUpdateWindowingModeWhenInLockedTask() { 448 launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 449 final WindowManagerState.ActivityTask task = 450 mWmState.getStandardRootTaskByWindowingMode( 451 WINDOWING_MODE_FULLSCREEN).getTopTask(); 452 453 try { 454 // Lock the task 455 runWithShellPermission(() -> mAtm.startSystemLockTaskMode(task.mTaskId)); 456 waitForOrFail("Fail to enter locked task mode", () -> 457 mAm.getLockTaskModeState() != LOCK_TASK_MODE_NONE); 458 459 // Verify specifying non-fullscreen windowing mode will fail. 460 boolean exceptionThrown = false; 461 try { 462 runWithShellPermission(() -> { 463 final WindowContainerTransaction wct = new WindowContainerTransaction() 464 .setWindowingMode( 465 mTaskOrganizer.getTaskInfo(task.mTaskId).getToken(), 466 WINDOWING_MODE_MULTI_WINDOW); 467 mTaskOrganizer.applyTransaction(wct); 468 }); 469 } catch (UnsupportedOperationException e) { 470 exceptionThrown = true; 471 } 472 assertTrue("Not allowed to specify windowing mode while in locked task mode.", 473 exceptionThrown); 474 } finally { 475 runWithShellPermission(() -> { 476 mAtm.stopSystemLockTaskMode(); 477 }); 478 } 479 } 480 481 @Test testDisallowHierarchyOperationWhenInLockedTask()482 public void testDisallowHierarchyOperationWhenInLockedTask() { 483 launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 484 launchActivity(LAUNCHING_ACTIVITY, WINDOWING_MODE_MULTI_WINDOW); 485 final WindowManagerState.ActivityTask task = mWmState 486 .getStandardRootTaskByWindowingMode(WINDOWING_MODE_FULLSCREEN).getTopTask(); 487 final WindowManagerState.ActivityTask root = mWmState 488 .getStandardRootTaskByWindowingMode(WINDOWING_MODE_MULTI_WINDOW).getTopTask(); 489 490 try { 491 // Lock the task 492 runWithShellPermission(() -> { 493 mAtm.startSystemLockTaskMode(task.mTaskId); 494 }); 495 waitForOrFail("Fail to enter locked task mode", () -> 496 mAm.getLockTaskModeState() != LOCK_TASK_MODE_NONE); 497 498 boolean gotAssertionError = false; 499 try { 500 runWithShellPermission(() -> { 501 // Fetch tokens of testing task and multi-window root. 502 final WindowContainerToken multiWindowRoot = 503 mTaskOrganizer.getTaskInfo(root.mTaskId).getToken(); 504 final WindowContainerToken testChild = 505 mTaskOrganizer.getTaskInfo(task.mTaskId).getToken(); 506 507 // Verify performing reparent operation is no operation. 508 final WindowContainerTransaction wct = new WindowContainerTransaction() 509 .reparent(testChild, multiWindowRoot, true /* onTop */); 510 mTaskOrganizer.applyTransaction(wct); 511 waitForOrFail("Fail to reparent", () -> 512 mTaskOrganizer.getTaskInfo(task.mTaskId).getParentTaskId() 513 == root.mTaskId); 514 }); 515 } catch (AssertionError e) { 516 gotAssertionError = true; 517 } 518 assertTrue("Not allowed to perform hierarchy operation while in locked task mode.", 519 gotAssertionError); 520 } finally { 521 runWithShellPermission(() -> { 522 mAtm.stopSystemLockTaskMode(); 523 }); 524 } 525 } 526 527 /** 528 * Asserts that the activity is visible when the top opaque activity finishes and with another 529 * translucent activity on top while in split-screen-secondary task. 530 */ 531 @Test testVisibilityWithTranslucentAndTopFinishingActivity()532 public void testVisibilityWithTranslucentAndTopFinishingActivity() { 533 // Launch two activities in split-screen mode. 534 launchActivitiesInSplitScreen( 535 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY), 536 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY)); 537 538 mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId()); 539 540 // Launch two more activities on a different task on top of split-screen-secondary and 541 // only the top opaque activity should be visible. 542 // Explicitly launch them into fullscreen mode because the control windowing mode of the 543 // launch root doesn't include freeform mode. Freeform first devices launch apps in freeform 544 // mode by default, which won't trigger the launch root. 545 getLaunchActivityBuilder().setTargetActivity(TRANSLUCENT_TEST_ACTIVITY) 546 .setUseInstrumentation() 547 .setWaitForLaunched(true) 548 .setWindowingMode(WINDOWING_MODE_FULLSCREEN) 549 .execute(); 550 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY) 551 .setUseInstrumentation() 552 .setWaitForLaunched(true) 553 .setWindowingMode(WINDOWING_MODE_FULLSCREEN) 554 .execute(); 555 mWmState.assertVisibility(TEST_ACTIVITY, true); 556 mWmState.waitForActivityState(TRANSLUCENT_TEST_ACTIVITY, STATE_STOPPED); 557 mWmState.assertVisibility(TRANSLUCENT_TEST_ACTIVITY, false); 558 mWmState.assertVisibility(TEST_ACTIVITY_WITH_SAME_AFFINITY, false); 559 560 // Finish the top opaque activity and both the two activities should be visible. 561 mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF); 562 mWmState.computeState(new WaitForValidActivityState(TRANSLUCENT_TEST_ACTIVITY)); 563 mWmState.assertVisibility(TRANSLUCENT_TEST_ACTIVITY, true); 564 mWmState.assertVisibility(TEST_ACTIVITY_WITH_SAME_AFFINITY, true); 565 } 566 } 567