1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; 22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 24 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 25 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 26 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 27 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; 28 import static android.view.Display.DEFAULT_DISPLAY; 29 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; 30 31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 34 import static com.android.server.wm.ActivityRecord.State.FINISHING; 35 import static com.android.server.wm.ActivityRecord.State.PAUSED; 36 import static com.android.server.wm.ActivityRecord.State.PAUSING; 37 import static com.android.server.wm.ActivityRecord.State.RESUMED; 38 import static com.android.server.wm.ActivityRecord.State.STOPPED; 39 import static com.android.server.wm.ActivityRecord.State.STOPPING; 40 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; 41 import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE; 42 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; 43 44 import static com.google.common.truth.Truth.assertThat; 45 46 import static org.junit.Assert.assertEquals; 47 import static org.junit.Assert.assertFalse; 48 import static org.junit.Assert.assertNotEquals; 49 import static org.junit.Assert.assertNotNull; 50 import static org.junit.Assert.assertNull; 51 import static org.junit.Assert.assertTrue; 52 import static org.mockito.ArgumentMatchers.any; 53 import static org.mockito.ArgumentMatchers.anyBoolean; 54 import static org.mockito.ArgumentMatchers.anyInt; 55 import static org.mockito.ArgumentMatchers.contains; 56 import static org.mockito.ArgumentMatchers.eq; 57 import static org.mockito.ArgumentMatchers.refEq; 58 import static org.mockito.Mockito.clearInvocations; 59 import static org.mockito.Mockito.mock; 60 import static org.mockito.Mockito.never; 61 import static org.mockito.Mockito.reset; 62 import static org.mockito.Mockito.spy; 63 import static org.mockito.Mockito.times; 64 import static org.mockito.Mockito.verify; 65 66 import android.app.ActivityOptions; 67 import android.app.WindowConfiguration; 68 import android.content.ComponentName; 69 import android.content.Intent; 70 import android.content.pm.ActivityInfo; 71 import android.content.pm.ApplicationInfo; 72 import android.content.pm.ResolveInfo; 73 import android.content.res.Configuration; 74 import android.content.res.Resources; 75 import android.graphics.Rect; 76 import android.os.PowerManager; 77 import android.os.RemoteException; 78 import android.os.UserHandle; 79 import android.platform.test.annotations.Presubmit; 80 import android.util.Pair; 81 82 import androidx.test.filters.MediumTest; 83 84 import com.android.internal.app.ResolverActivity; 85 86 import org.junit.Before; 87 import org.junit.Test; 88 import org.junit.runner.RunWith; 89 90 import java.util.ArrayList; 91 import java.util.Arrays; 92 import java.util.List; 93 import java.util.function.Consumer; 94 95 /** 96 * Tests for {@link RootWindowContainer}. 97 * 98 * Build/Install/Run: 99 * atest WmTests:RootWindowContainerTests 100 */ 101 @MediumTest 102 @Presubmit 103 @RunWith(WindowTestRunner.class) 104 public class RootWindowContainerTests extends WindowTestsBase { 105 106 @Before setUp()107 public void setUp() throws Exception { 108 doNothing().when(mAtm).updateSleepIfNeededLocked(); 109 } 110 111 @Test testUpdateDefaultTaskDisplayAreaWindowingModeOnSettingsRetrieved()112 public void testUpdateDefaultTaskDisplayAreaWindowingModeOnSettingsRetrieved() { 113 assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, 114 mWm.getDefaultDisplayContentLocked().getDefaultTaskDisplayArea() 115 .getWindowingMode()); 116 117 mWm.mIsPc = true; 118 mWm.mAtmService.mSupportsFreeformWindowManagement = true; 119 120 mWm.mRoot.onSettingsRetrieved(); 121 122 assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM, 123 mWm.getDefaultDisplayContentLocked().getDefaultTaskDisplayArea() 124 .getWindowingMode()); 125 } 126 127 /** 128 * This test ensures that an existing single instance activity with alias name can be found by 129 * the same activity info. So {@link ActivityStarter#getReusableTask} won't miss it that leads 130 * to create an unexpected new instance. 131 */ 132 @Test testFindActivityByTargetComponent()133 public void testFindActivityByTargetComponent() { 134 final ComponentName aliasComponent = ComponentName.createRelative( 135 DEFAULT_COMPONENT_PACKAGE_NAME, ".AliasActivity"); 136 final ComponentName targetComponent = ComponentName.createRelative( 137 aliasComponent.getPackageName(), ".TargetActivity"); 138 final ActivityRecord activity = new ActivityBuilder(mWm.mAtmService) 139 .setComponent(aliasComponent) 140 .setTargetActivity(targetComponent.getClassName()) 141 .setLaunchMode(ActivityInfo.LAUNCH_SINGLE_INSTANCE) 142 .setCreateTask(true) 143 .build(); 144 145 assertEquals(activity, mWm.mRoot.findActivity(activity.intent, activity.info, 146 false /* compareIntentFilters */)); 147 } 148 149 @Test testFindTask_includeLaunchedFromBubbled()150 public void testFindTask_includeLaunchedFromBubbled() { 151 final ComponentName component = ComponentName.createRelative( 152 DEFAULT_COMPONENT_PACKAGE_NAME, ".BubbledActivity"); 153 final ActivityOptions opts = ActivityOptions.makeBasic(); 154 opts.setTaskAlwaysOnTop(true); 155 opts.setLaunchedFromBubble(true); 156 final ActivityRecord activity = new ActivityBuilder(mWm.mAtmService) 157 .setComponent(component) 158 .setActivityOptions(opts) 159 .setCreateTask(true) 160 .build(); 161 162 assertEquals(activity, mWm.mRoot.findTask(activity, activity.getTaskDisplayArea(), 163 true /* includeLaunchedFromBubble */)); 164 } 165 166 @Test testFindTask_ignoreLaunchedFromBubbled()167 public void testFindTask_ignoreLaunchedFromBubbled() { 168 final ComponentName component = ComponentName.createRelative( 169 DEFAULT_COMPONENT_PACKAGE_NAME, ".BubbledActivity"); 170 final ActivityOptions opts = ActivityOptions.makeBasic(); 171 opts.setTaskAlwaysOnTop(true); 172 opts.setLaunchedFromBubble(true); 173 final ActivityRecord activity = new ActivityBuilder(mWm.mAtmService) 174 .setComponent(component) 175 .setActivityOptions(opts) 176 .setCreateTask(true) 177 .build(); 178 179 assertNull(mWm.mRoot.findTask(activity, activity.getTaskDisplayArea(), 180 false /* includeLaunchedFromBubble */)); 181 } 182 183 @Test testAllPausedActivitiesComplete()184 public void testAllPausedActivitiesComplete() { 185 DisplayContent displayContent = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY); 186 ActivityRecord activity = createActivityRecord(displayContent); 187 Task task = activity.getTask(); 188 task.setPausingActivity(activity); 189 190 activity.setState(PAUSING, "test PAUSING"); 191 assertThat(mWm.mRoot.allPausedActivitiesComplete()).isFalse(); 192 193 activity.setState(PAUSED, "test PAUSED"); 194 assertThat(mWm.mRoot.allPausedActivitiesComplete()).isTrue(); 195 196 activity.setState(STOPPED, "test STOPPED"); 197 assertThat(mWm.mRoot.allPausedActivitiesComplete()).isTrue(); 198 199 activity.setState(STOPPING, "test STOPPING"); 200 assertThat(mWm.mRoot.allPausedActivitiesComplete()).isTrue(); 201 202 activity.setState(FINISHING, "test FINISHING"); 203 assertThat(mWm.mRoot.allPausedActivitiesComplete()).isTrue(); 204 } 205 206 @Test testTaskLayerRank()207 public void testTaskLayerRank() { 208 final Task rootTask = new TaskBuilder(mSupervisor).build(); 209 final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); 210 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task1).build(); 211 activity1.setVisibleRequested(true); 212 mWm.mRoot.rankTaskLayers(); 213 214 assertEquals(1, task1.mLayerRank); 215 // Only tasks that directly contain activities have a ranking. 216 assertEquals(Task.LAYER_RANK_INVISIBLE, rootTask.mLayerRank); 217 218 final Task task2 = new TaskBuilder(mSupervisor).build(); 219 final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task2).build(); 220 activity2.setVisibleRequested(true); 221 mWm.mRoot.rankTaskLayers(); 222 223 // Note that ensureActivitiesVisible is disabled in SystemServicesTestRule, so both the 224 // activities have the visible rank. 225 assertEquals(2, task1.mLayerRank); 226 // The task2 is the top task, so it has a lower rank as a higher priority oom score. 227 assertEquals(1, task2.mLayerRank); 228 229 task2.moveToBack("test", null /* task */); 230 // RootWindowContainer#invalidateTaskLayers should post to update. 231 waitHandlerIdle(mWm.mH); 232 233 assertEquals(1, task1.mLayerRank); 234 assertEquals(2, task2.mLayerRank); 235 236 // The rank should be updated to invisible when device went to sleep. 237 activity1.setVisibleRequested(false); 238 activity2.setVisibleRequested(false); 239 doReturn(true).when(mAtm).isSleepingOrShuttingDownLocked(); 240 doReturn(true).when(mRootWindowContainer).putTasksToSleep(anyBoolean(), anyBoolean()); 241 mSupervisor.mGoingToSleepWakeLock = mock(PowerManager.WakeLock.class); 242 doReturn(false).when(mSupervisor.mGoingToSleepWakeLock).isHeld(); 243 mAtm.mTaskSupervisor.checkReadyForSleepLocked(false /* allowDelay */); 244 assertEquals(Task.LAYER_RANK_INVISIBLE, task1.mLayerRank); 245 assertEquals(Task.LAYER_RANK_INVISIBLE, task2.mLayerRank); 246 } 247 248 @Test testForceStopPackage()249 public void testForceStopPackage() { 250 final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); 251 final ActivityRecord activity = task.getTopMostActivity(); 252 final WindowProcessController wpc = activity.app; 253 final ActivityRecord[] activities = { 254 activity, 255 new ActivityBuilder(mWm.mAtmService).setTask(task).setUseProcess(wpc).build(), 256 new ActivityBuilder(mWm.mAtmService).setTask(task).setUseProcess(wpc).build() 257 }; 258 activities[0].detachFromProcess(); 259 activities[1].finishing = true; 260 activities[1].destroyImmediately("test"); 261 spyOn(wpc); 262 doReturn(true).when(wpc).isRemoved(); 263 264 mWm.mAtmService.mInternal.onForceStopPackage(wpc.mInfo.packageName, true /* doit */, 265 false /* evenPersistent */, wpc.mUserId); 266 // The activity without process should be removed. 267 assertEquals(2, task.getChildCount()); 268 269 wpc.handleAppDied(); 270 // The activities with process should be removed because WindowProcessController#isRemoved. 271 assertFalse(task.hasChild()); 272 assertFalse(wpc.hasActivities()); 273 } 274 275 @Test testAttachApplication()276 public void testAttachApplication() { 277 final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); 278 activity.detachFromProcess(); 279 mAtm.startProcessAsync(activity, false /* knownToBeDead */, 280 true /* isTop */, "test" /* hostingType */); 281 final WindowProcessController proc = mSystemServicesTestRule.addProcess( 282 activity.packageName, activity.processName, 283 6789 /* pid */, activity.info.applicationInfo.uid); 284 try { 285 mRootWindowContainer.attachApplication(proc); 286 verify(mSupervisor).realStartActivityLocked(eq(activity), eq(proc), anyBoolean(), 287 anyBoolean()); 288 } catch (RemoteException e) { 289 e.rethrowAsRuntimeException(); 290 } 291 } 292 293 /** 294 * This test ensures that we do not try to restore a task based off an invalid task id. We 295 * should expect {@code null} to be returned in this case. 296 */ 297 @Test testRestoringInvalidTask()298 public void testRestoringInvalidTask() { 299 mRootWindowContainer.getDefaultDisplay().removeAllTasks(); 300 Task task = mRootWindowContainer.anyTaskForId(0 /*taskId*/, 301 MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */); 302 assertNull(task); 303 } 304 305 /** 306 * This test ensures that an existing task in the pinned root task is moved to the fullscreen 307 * activity root task when a new task is added. 308 */ 309 @Test testReplacingTaskInPinnedRootTask()310 public void testReplacingTaskInPinnedRootTask() { 311 Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( 312 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); 313 final ActivityRecord firstActivity = new ActivityBuilder(mAtm) 314 .setTask(fullscreenTask).build(); 315 final ActivityRecord secondActivity = new ActivityBuilder(mAtm) 316 .setTask(fullscreenTask).build(); 317 318 fullscreenTask.moveToFront("testReplacingTaskInPinnedRootTask"); 319 320 // Ensure full screen root task has both tasks. 321 ensureTaskPlacement(fullscreenTask, firstActivity, secondActivity); 322 323 // Move first activity to pinned root task. 324 mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, 325 null /* launchIntoPipHostActivity */, "initialMove"); 326 327 final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea(); 328 Task pinnedRootTask = taskDisplayArea.getRootPinnedTask(); 329 // Ensure a task has moved over. 330 ensureTaskPlacement(pinnedRootTask, firstActivity); 331 ensureTaskPlacement(fullscreenTask, secondActivity); 332 333 // Move second activity to pinned root task. 334 mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, 335 null /* launchIntoPipHostActivity */, "secondMove"); 336 337 // Need to get root tasks again as a new instance might have been created. 338 pinnedRootTask = taskDisplayArea.getRootPinnedTask(); 339 fullscreenTask = taskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN, 340 ACTIVITY_TYPE_STANDARD); 341 // Ensure root tasks have swapped tasks. 342 ensureTaskPlacement(pinnedRootTask, secondActivity); 343 ensureTaskPlacement(fullscreenTask, firstActivity); 344 } 345 346 @Test testMovingBottomMostRootTaskActivityToPinnedRootTask()347 public void testMovingBottomMostRootTaskActivityToPinnedRootTask() { 348 final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( 349 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); 350 final ActivityRecord firstActivity = new ActivityBuilder(mAtm) 351 .setTask(fullscreenTask).build(); 352 final Task task = firstActivity.getTask(); 353 354 final ActivityRecord secondActivity = new ActivityBuilder(mAtm) 355 .setTask(fullscreenTask).build(); 356 357 fullscreenTask.moveTaskToBack(task); 358 359 // Ensure full screen task has both tasks. 360 ensureTaskPlacement(fullscreenTask, firstActivity, secondActivity); 361 assertEquals(task.getTopMostActivity(), secondActivity); 362 firstActivity.setState(STOPPED, "testMovingBottomMostRootTaskActivityToPinnedRootTask"); 363 364 365 // Move first activity to pinned root task. 366 mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, 367 null /* launchIntoPipHostActivity */, "initialMove"); 368 369 assertTrue(firstActivity.mRequestForceTransition); 370 } 371 372 @Test testMultipleActivitiesTaskEnterPip()373 public void testMultipleActivitiesTaskEnterPip() { 374 // Enable shell transition because the order of setting windowing mode is different. 375 registerTestTransitionPlayer(); 376 final ActivityRecord transientActivity = new ActivityBuilder(mAtm) 377 .setCreateTask(true).build(); 378 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build(); 379 final ActivityRecord activity2 = new ActivityBuilder(mAtm) 380 .setTask(activity1.getTask()).build(); 381 activity2.setState(RESUMED, "test"); 382 383 // Assume the top activity switches to a transient-launch, e.g. recents. 384 transientActivity.setState(RESUMED, "test"); 385 transientActivity.getTask().moveToFront("test"); 386 387 mRootWindowContainer.moveActivityToPinnedRootTask(activity2, 388 null /* launchIntoPipHostActivity */, "test"); 389 assertEquals("Created PiP task must not change focus", transientActivity.getTask(), 390 mRootWindowContainer.getTopDisplayFocusedRootTask()); 391 final Task newPipTask = activity2.getTask(); 392 assertEquals(newPipTask, mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask()); 393 assertNotEquals(newPipTask, activity1.getTask()); 394 assertFalse("Created PiP task must not be in recents", newPipTask.inRecents); 395 assertThat(newPipTask.autoRemoveRecents).isTrue(); 396 assertThat(activity1.getTask().autoRemoveRecents).isFalse(); 397 } 398 399 /** 400 * When there is only one activity in the Task, and the activity is requesting to enter PIP, the 401 * whole Task should enter PIP. 402 */ 403 @Test testSingleActivityTaskEnterPip()404 public void testSingleActivityTaskEnterPip() { 405 final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( 406 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); 407 final ActivityRecord activity = new ActivityBuilder(mAtm) 408 .setTask(fullscreenTask) 409 .build(); 410 final Task task = activity.getTask(); 411 412 // Move activity to pinned root task. 413 mRootWindowContainer.moveActivityToPinnedRootTask(activity, 414 null /* launchIntoPipHostActivity */, "test"); 415 416 // Ensure a task has moved over. 417 ensureTaskPlacement(task, activity); 418 assertTrue(task.inPinnedWindowingMode()); 419 assertFalse("Entering PiP activity must not affect SysUiFlags", 420 activity.canAffectSystemUiFlags()); 421 422 // The activity with fixed orientation should not apply letterbox when entering PiP. 423 final int requestedOrientation = task.getConfiguration().orientation 424 == Configuration.ORIENTATION_PORTRAIT 425 ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT; 426 doReturn(requestedOrientation).when(activity).getRequestedConfigurationOrientation(); 427 doReturn(false).when(activity).handlesOrientationChangeFromDescendant(anyInt()); 428 final Rect bounds = new Rect(task.getBounds()); 429 bounds.scale(0.5f); 430 task.setBounds(bounds); 431 assertFalse(activity.isLetterboxedForFixedOrientationAndAspectRatio()); 432 assertThat(task.autoRemoveRecents).isFalse(); 433 } 434 435 /** 436 * When there is only one activity in the Task, and the activity is requesting to enter PIP, the 437 * whole Task should enter PIP even if the activity is in a TaskFragment. 438 */ 439 @Test testSingleActivityInTaskFragmentEnterPip()440 public void testSingleActivityInTaskFragmentEnterPip() { 441 final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( 442 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); 443 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 444 .setParentTask(fullscreenTask) 445 .createActivityCount(1) 446 .build(); 447 final ActivityRecord activity = taskFragment.getTopMostActivity(); 448 final Task task = activity.getTask(); 449 450 // Move activity to pinned root task. 451 mRootWindowContainer.moveActivityToPinnedRootTask(activity, 452 null /* launchIntoPipHostActivity */, "test"); 453 454 // Ensure a task has moved over. 455 ensureTaskPlacement(task, activity); 456 assertTrue(task.inPinnedWindowingMode()); 457 assertThat(task.autoRemoveRecents).isFalse(); 458 } 459 460 /** 461 * When there is one TaskFragment with two activities in the Task, the activity requests to 462 * enter PIP, that activity will be move to PIP root task. 463 */ 464 @Test testMultipleActivitiesInTaskFragmentEnterPip()465 public void testMultipleActivitiesInTaskFragmentEnterPip() { 466 final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( 467 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); 468 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 469 .setParentTask(fullscreenTask) 470 .createActivityCount(2) 471 .build(); 472 final ActivityRecord firstActivity = taskFragment.getTopMostActivity(); 473 final ActivityRecord secondActivity = taskFragment.getBottomMostActivity(); 474 475 // Move first activity to pinned root task. 476 mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, 477 null /* launchIntoPipHostActivity */, "test"); 478 479 final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea(); 480 final Task pinnedRootTask = taskDisplayArea.getRootPinnedTask(); 481 482 // Ensure a task has moved over. 483 ensureTaskPlacement(pinnedRootTask, firstActivity); 484 ensureTaskPlacement(fullscreenTask, secondActivity); 485 assertTrue(pinnedRootTask.inPinnedWindowingMode()); 486 assertEquals(WINDOWING_MODE_FULLSCREEN, fullscreenTask.getWindowingMode()); 487 assertThat(pinnedRootTask.autoRemoveRecents).isTrue(); 488 assertThat(secondActivity.getTask().autoRemoveRecents).isFalse(); 489 } 490 491 @Test testMovingEmbeddedActivityToPip()492 public void testMovingEmbeddedActivityToPip() { 493 final Rect taskBounds = new Rect(0, 0, 800, 1000); 494 final Rect taskFragmentBounds = new Rect(0, 0, 400, 1000); 495 final Task task = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( 496 WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, true /* onTop */); 497 task.setBounds(taskBounds); 498 assertEquals(taskBounds, task.getBounds()); 499 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 500 .setParentTask(task) 501 .createActivityCount(2) 502 .setBounds(taskFragmentBounds) 503 .build(); 504 assertEquals(taskFragmentBounds, taskFragment.getBounds()); 505 final ActivityRecord topActivity = taskFragment.getTopMostActivity(); 506 507 // Move the top activity to pinned root task. 508 mRootWindowContainer.moveActivityToPinnedRootTask(topActivity, 509 null /* launchIntoPipHostActivity */, "test"); 510 511 final Task pinnedRootTask = task.getDisplayArea().getRootPinnedTask(); 512 513 // Ensure the initial bounds of the PiP Task is the same as the TaskFragment. 514 ensureTaskPlacement(pinnedRootTask, topActivity); 515 assertEquals(taskFragmentBounds, pinnedRootTask.getBounds()); 516 } 517 ensureTaskPlacement(Task task, ActivityRecord... activities)518 private static void ensureTaskPlacement(Task task, ActivityRecord... activities) { 519 final ArrayList<ActivityRecord> taskActivities = new ArrayList<>(); 520 521 task.forAllActivities((Consumer<ActivityRecord>) taskActivities::add, false); 522 523 assertEquals("Expecting " + Arrays.deepToString(activities) + " got " + taskActivities, 524 taskActivities.size(), activities != null ? activities.length : 0); 525 526 if (activities == null) { 527 return; 528 } 529 530 for (ActivityRecord activity : activities) { 531 assertTrue(taskActivities.contains(activity)); 532 } 533 } 534 535 @Test testApplySleepTokens()536 public void testApplySleepTokens() { 537 final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); 538 final KeyguardController keyguard = mSupervisor.getKeyguardController(); 539 final Task task = new TaskBuilder(mSupervisor) 540 .setDisplay(display) 541 .setOnTop(false) 542 .build(); 543 544 // Make sure we wake and resume in the case the display is turning on and the keyguard is 545 // not showing. 546 verifySleepTokenBehavior(display, keyguard, task, true /*displaySleeping*/, 547 false /* displayShouldSleep */, true /* isFocusedTask */, 548 false /* keyguardShowing */, true /* expectWakeFromSleep */, 549 true /* expectResumeTopActivity */); 550 551 // Make sure we wake and don't resume when the display is turning on and the keyguard is 552 // showing. 553 verifySleepTokenBehavior(display, keyguard, task, true /*displaySleeping*/, 554 false /* displayShouldSleep */, true /* isFocusedTask */, 555 true /* keyguardShowing */, true /* expectWakeFromSleep */, 556 false /* expectResumeTopActivity */); 557 558 // Make sure we wake and don't resume when the display is turning on and the keyguard is 559 // not showing as unfocused. 560 verifySleepTokenBehavior(display, keyguard, task, true /*displaySleeping*/, 561 false /* displayShouldSleep */, false /* isFocusedTask */, 562 false /* keyguardShowing */, true /* expectWakeFromSleep */, 563 false /* expectResumeTopActivity */); 564 565 // Should not do anything if the display state hasn't changed. 566 verifySleepTokenBehavior(display, keyguard, task, false /*displaySleeping*/, 567 false /* displayShouldSleep */, true /* isFocusedTask */, 568 false /* keyguardShowing */, false /* expectWakeFromSleep */, 569 false /* expectResumeTopActivity */); 570 } 571 verifySleepTokenBehavior(DisplayContent display, KeyguardController keyguard, Task task, boolean displaySleeping, boolean displayShouldSleep, boolean isFocusedTask, boolean keyguardShowing, boolean expectWakeFromSleep, boolean expectResumeTopActivity)572 private void verifySleepTokenBehavior(DisplayContent display, KeyguardController keyguard, 573 Task task, boolean displaySleeping, boolean displayShouldSleep, 574 boolean isFocusedTask, boolean keyguardShowing, boolean expectWakeFromSleep, 575 boolean expectResumeTopActivity) { 576 reset(task); 577 578 doReturn(displayShouldSleep).when(display).shouldSleep(); 579 doReturn(displaySleeping).when(display).isSleeping(); 580 doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt()); 581 582 doReturn(isFocusedTask).when(task).isFocusedRootTaskOnDisplay(); 583 doReturn(isFocusedTask ? task : null).when(display).getFocusedRootTask(); 584 TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea(); 585 doReturn(isFocusedTask ? task : null).when(defaultTaskDisplayArea).getFocusedRootTask(); 586 mRootWindowContainer.applySleepTokens(true); 587 verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleeping(); 588 verify(task, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked( 589 null /* target */, null /* targetOptions */); 590 } 591 592 @Test testAwakeFromSleepingWithAppConfiguration()593 public void testAwakeFromSleepingWithAppConfiguration() { 594 final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); 595 final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); 596 activity.moveFocusableActivityToTop("test"); 597 assertTrue(activity.getRootTask().isFocusedRootTaskOnDisplay()); 598 ActivityRecordTests.setRotatedScreenOrientationSilently(activity); 599 600 final Configuration rotatedConfig = new Configuration(); 601 display.computeScreenConfiguration(rotatedConfig, display.getDisplayRotation() 602 .rotationForOrientation(activity.getOrientation(), display.getRotation())); 603 assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation); 604 // Assume the activity was shown in different orientation. For example, the top activity is 605 // landscape and the portrait lockscreen is shown. 606 activity.setLastReportedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig); 607 activity.setState(STOPPED, "sleep"); 608 609 display.setIsSleeping(true); 610 doReturn(false).when(display).shouldSleep(); 611 // Allow to resume when awaking. 612 setBooted(mAtm); 613 mRootWindowContainer.applySleepTokens(true); 614 615 // The display orientation should be changed by the activity so there is no relaunch. 616 verify(activity, never()).relaunchActivityLocked(anyBoolean(), anyInt()); 617 assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation); 618 } 619 620 /** 621 * Verifies that removal of activity with task and root task is done correctly. 622 */ 623 @Test testRemovingRootTaskOnAppCrash()624 public void testRemovingRootTaskOnAppCrash() { 625 final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer 626 .getDefaultTaskDisplayArea(); 627 final int originalRootTaskCount = defaultTaskDisplayArea.getRootTaskCount(); 628 final Task rootTask = defaultTaskDisplayArea.createRootTask( 629 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 630 final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(rootTask).build(); 631 632 assertEquals(originalRootTaskCount + 1, defaultTaskDisplayArea.getRootTaskCount()); 633 634 // Let's pretend that the app has crashed. 635 firstActivity.app.setThread(null); 636 final Task finishedTask = mRootWindowContainer.finishTopCrashedActivities( 637 firstActivity.app, "test"); 638 639 // Verify that the root task was removed. 640 assertEquals(originalRootTaskCount, defaultTaskDisplayArea.getRootTaskCount()); 641 assertEquals(rootTask, finishedTask); 642 } 643 644 /** 645 * Verifies that removal of activities with task and root task is done correctly when there are 646 * several task display areas. 647 */ 648 @Test testRemovingRootTaskOnAppCrash_multipleDisplayAreas()649 public void testRemovingRootTaskOnAppCrash_multipleDisplayAreas() { 650 final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer 651 .getDefaultTaskDisplayArea(); 652 final int originalRootTaskCount = defaultTaskDisplayArea.getRootTaskCount(); 653 final Task rootTask = defaultTaskDisplayArea.createRootTask( 654 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 655 final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(rootTask).build(); 656 assertEquals(originalRootTaskCount + 1, defaultTaskDisplayArea.getRootTaskCount()); 657 658 final DisplayContent dc = defaultTaskDisplayArea.getDisplayContent(); 659 final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( 660 dc, mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST); 661 final Task secondRootTask = secondTaskDisplayArea.createRootTask( 662 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 663 new ActivityBuilder(mAtm).setTask(secondRootTask).setUseProcess(firstActivity.app).build(); 664 assertEquals(1, secondTaskDisplayArea.getRootTaskCount()); 665 666 // Let's pretend that the app has crashed. 667 firstActivity.app.setThread(null); 668 mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test"); 669 670 // Verify that the root tasks were removed. 671 assertEquals(originalRootTaskCount, defaultTaskDisplayArea.getRootTaskCount()); 672 assertEquals(0, secondTaskDisplayArea.getRootTaskCount()); 673 } 674 675 @Test testFocusability()676 public void testFocusability() { 677 final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer 678 .getDefaultTaskDisplayArea(); 679 final Task task = defaultTaskDisplayArea.createRootTask( 680 WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, true /* onTop */); 681 final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); 682 683 // Created tasks are focusable by default. 684 assertTrue(task.isTopActivityFocusable()); 685 assertTrue(activity.isFocusable()); 686 687 // If the task is made unfocusable, its activities should inherit that. 688 task.setFocusable(false); 689 assertFalse(task.isTopActivityFocusable()); 690 assertFalse(activity.isFocusable()); 691 692 final Task pinnedTask = defaultTaskDisplayArea.createRootTask( 693 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); 694 final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm) 695 .setTask(pinnedTask).build(); 696 697 // We should not be focusable when in pinned mode 698 assertFalse(pinnedTask.isTopActivityFocusable()); 699 assertFalse(pinnedActivity.isFocusable()); 700 701 // Add flag forcing focusability. 702 pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE; 703 704 // Task with FLAG_ALWAYS_FOCUSABLE should be focusable. 705 assertTrue(pinnedTask.isTopActivityFocusable()); 706 assertTrue(pinnedActivity.isFocusable()); 707 } 708 709 /** 710 * Verify that home root task would be moved to front when the top activity is Recents. 711 */ 712 @Test testFindTaskToMoveToFrontWhenRecentsOnTop()713 public void testFindTaskToMoveToFrontWhenRecentsOnTop() { 714 // Create root task/task on default display. 715 final Task targetRootTask = new TaskBuilder(mSupervisor) 716 .setCreateActivity(true) 717 .setOnTop(false) 718 .build(); 719 final Task targetTask = targetRootTask.getBottomMostTask(); 720 721 // Create Recents on top of the display. 722 final Task rootTask = new TaskBuilder(mSupervisor) 723 .setCreateActivity(true) 724 .setActivityType(ACTIVITY_TYPE_RECENTS) 725 .build(); 726 727 final String reason = "findTaskToMoveToFront"; 728 mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, 729 false); 730 731 final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); 732 verify(taskDisplayArea).moveHomeRootTaskToFront(contains(reason)); 733 } 734 735 /** 736 * Verify that home root task won't be moved to front if the top activity on other display is 737 * Recents. 738 */ 739 @Test testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay()740 public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() { 741 // Create tasks on default display. 742 final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); 743 final Task targetRootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, 744 ACTIVITY_TYPE_STANDARD, false /* onTop */); 745 final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetRootTask) 746 .build(); 747 748 // Create Recents on secondary display. 749 final TestDisplayContent secondDisplay = addNewDisplayContentAt( 750 DisplayContent.POSITION_TOP); 751 final Task rootTask = secondDisplay.getDefaultTaskDisplayArea() 752 .createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); 753 new ActivityBuilder(mAtm).setTask(rootTask).build(); 754 755 final String reason = "findTaskToMoveToFront"; 756 mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, 757 false); 758 759 verify(taskDisplayArea, never()).moveHomeRootTaskToFront(contains(reason)); 760 } 761 762 /** 763 * Verify if a root task is not at the topmost position, it should be able to resume its 764 * activity if the root task is the top focused. 765 */ 766 @Test testResumeActivityWhenNonTopmostRootTaskIsTopFocused()767 public void testResumeActivityWhenNonTopmostRootTaskIsTopFocused() { 768 // Create a root task at bottom. 769 final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); 770 final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, 771 ACTIVITY_TYPE_STANDARD, false /* onTop */)); 772 final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build(); 773 taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/); 774 775 // Assume the task is not at the topmost position (e.g. behind always-on-top root tasks) 776 // but it is the current top focused task. 777 assertFalse(rootTask.isTopRootTaskInDisplayArea()); 778 doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask(); 779 780 // Use the task as target to resume. 781 mRootWindowContainer.resumeFocusedTasksTopActivities(rootTask, activity, 782 null /* targetOptions */); 783 784 // Verify the target task should resume its activity. 785 verify(rootTask, times(1)).resumeTopActivityUncheckedLocked( 786 eq(activity), eq(null /* targetOptions */), eq(false)); 787 } 788 789 /** 790 * Verify that home activity will be started on a display even if another display has a 791 * focusable activity. 792 */ 793 @Test testResumeFocusedRootTasksStartsHomeActivity_NoActivities()794 public void testResumeFocusedRootTasksStartsHomeActivity_NoActivities() { 795 final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); 796 taskDisplayArea.getRootHomeTask().removeIfPossible(); 797 taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); 798 799 doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any()); 800 801 setBooted(mAtm); 802 803 // Trigger resume on all displays 804 mRootWindowContainer.resumeFocusedTasksTopActivities(); 805 806 // Verify that home activity was started on the default display 807 verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea)); 808 } 809 810 /** 811 * Verify that home activity will be started on a display even if another display has a 812 * focusable activity. 813 */ 814 @Test testResumeFocusedRootTasksStartsHomeActivity_ActivityOnSecondaryScreen()815 public void testResumeFocusedRootTasksStartsHomeActivity_ActivityOnSecondaryScreen() { 816 final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); 817 taskDisplayArea.getRootHomeTask().removeIfPossible(); 818 taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); 819 820 // Create an activity on secondary display. 821 final TestDisplayContent secondDisplay = addNewDisplayContentAt( 822 DisplayContent.POSITION_TOP); 823 final Task rootTask = secondDisplay.getDefaultTaskDisplayArea().createRootTask( 824 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); 825 new ActivityBuilder(mAtm).setTask(rootTask).build(); 826 827 doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any()); 828 829 setBooted(mAtm); 830 831 // Trigger resume on all displays 832 mRootWindowContainer.resumeFocusedTasksTopActivities(); 833 834 // Verify that home activity was started on the default display 835 verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea)); 836 } 837 838 /** 839 * Verify that a lingering transition is being executed in case the activity to be resumed is 840 * already resumed 841 */ 842 @Test testResumeActivityLingeringTransition()843 public void testResumeActivityLingeringTransition() { 844 // Create a root task at top. 845 final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); 846 final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, 847 ACTIVITY_TYPE_STANDARD, false /* onTop */)); 848 final ActivityRecord activity = new ActivityBuilder(mAtm) 849 .setTask(rootTask).setOnTop(true).build(); 850 activity.setState(RESUMED, "test"); 851 852 // Assume the task is at the topmost position 853 assertTrue(rootTask.isTopRootTaskInDisplayArea()); 854 855 // Use the task as target to resume. 856 mRootWindowContainer.resumeFocusedTasksTopActivities(); 857 858 // Verify the lingering app transition is being executed because it's already resumed 859 verify(rootTask, times(1)).executeAppTransition(any()); 860 } 861 862 @Test testResumeActivityLingeringTransition_notExecuted()863 public void testResumeActivityLingeringTransition_notExecuted() { 864 // Create a root task at bottom. 865 final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); 866 final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, 867 ACTIVITY_TYPE_STANDARD, false /* onTop */)); 868 final ActivityRecord activity = new ActivityBuilder(mAtm) 869 .setTask(rootTask).setOnTop(true).build(); 870 activity.setState(RESUMED, "test"); 871 taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/); 872 873 // Assume the task is at the topmost position 874 assertFalse(rootTask.isTopRootTaskInDisplayArea()); 875 doReturn(taskDisplayArea.getHomeActivity()).when(taskDisplayArea).topRunningActivity( 876 anyBoolean()); 877 878 // Use the task as target to resume. 879 mRootWindowContainer.resumeFocusedTasksTopActivities(); 880 881 // Verify the lingering app transition is being executed because it's already resumed 882 verify(rootTask, never()).executeAppTransition(any()); 883 } 884 885 /** 886 * Tests that home activities can be started on the displays that supports system decorations. 887 */ 888 @Test testStartHomeOnAllDisplays()889 public void testStartHomeOnAllDisplays() { 890 mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); 891 mockResolveSecondaryHomeActivity(); 892 893 // Create secondary displays. 894 final TestDisplayContent secondDisplay = 895 new TestDisplayContent.Builder(mAtm, 1000, 1500) 896 .setSystemDecorations(true).build(); 897 898 doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(), 899 anyBoolean()); 900 901 mRootWindowContainer.startHomeOnAllDisplays(0, "testStartHome"); 902 903 assertTrue(mRootWindowContainer.getDefaultDisplay().getTopRootTask().isActivityTypeHome()); 904 assertNotNull(secondDisplay.getTopRootTask()); 905 assertTrue(secondDisplay.getTopRootTask().isActivityTypeHome()); 906 } 907 908 /** 909 * Tests that home activities won't be started before booting when display added. 910 */ 911 @Test testNotStartHomeBeforeBoot()912 public void testNotStartHomeBeforeBoot() { 913 final int displayId = 1; 914 doReturn(false).when(mAtm).isBooting(); 915 doReturn(false).when(mAtm).isBooted(); 916 mRootWindowContainer.onDisplayAdded(displayId); 917 verify(mRootWindowContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt()); 918 } 919 920 /** 921 * Tests whether home can be started if being instrumented. 922 */ 923 @Test testCanStartHomeWhenInstrumented()924 public void testCanStartHomeWhenInstrumented() { 925 final ActivityInfo info = new ActivityInfo(); 926 info.applicationInfo = new ApplicationInfo(); 927 final WindowProcessController app = mock(WindowProcessController.class); 928 doReturn(app).when(mAtm).getProcessController(any(), anyInt()); 929 930 // Can not start home if we don't want to start home while home is being instrumented. 931 doReturn(true).when(app).isInstrumenting(); 932 final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer 933 .getDefaultTaskDisplayArea(); 934 assertFalse(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea, 935 false /* allowInstrumenting*/)); 936 937 // Can start home for other cases. 938 assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea, 939 true /* allowInstrumenting*/)); 940 941 doReturn(false).when(app).isInstrumenting(); 942 assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea, 943 false /* allowInstrumenting*/)); 944 assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea, 945 true /* allowInstrumenting*/)); 946 } 947 948 /** 949 * Tests whether home can be started if it's not allowed by policy. 950 */ 951 @Test testCanStartHome_returnsFalse_ifDisallowedByPolicy()952 public void testCanStartHome_returnsFalse_ifDisallowedByPolicy() { 953 final ActivityInfo info = new ActivityInfo(); 954 info.applicationInfo = new ApplicationInfo(); 955 final WindowProcessController app = mock(WindowProcessController.class); 956 doReturn(app).when(mAtm).getProcessController(any(), anyInt()); 957 doReturn(false).when(app).isInstrumenting(); 958 final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); 959 doReturn(false).when(taskDisplayArea).canHostHomeTask(); 960 961 assertFalse(mRootWindowContainer.canStartHomeOnDisplayArea(info, taskDisplayArea, 962 false /* allowInstrumenting*/)); 963 } 964 965 966 /** 967 * Tests that secondary home activity should not be resolved if device is still locked. 968 */ 969 @Test testStartSecondaryHomeOnDisplayWithUserKeyLocked()970 public void testStartSecondaryHomeOnDisplayWithUserKeyLocked() { 971 // Create secondary displays. 972 final TestDisplayContent secondDisplay = 973 new TestDisplayContent.Builder(mAtm, 1000, 1500) 974 .setSystemDecorations(true).build(); 975 976 // Use invalid user id to let StorageManager.isCeStorageUnlocked() return false. 977 final int currentUser = mRootWindowContainer.mCurrentUser; 978 mRootWindowContainer.mCurrentUser = -1; 979 980 mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome", 981 secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */); 982 983 try { 984 verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any()); 985 } finally { 986 mRootWindowContainer.mCurrentUser = currentUser; 987 } 988 } 989 990 /** 991 * Tests that secondary home activity should not be resolved if display does not support system 992 * decorations. 993 */ 994 @Test testStartSecondaryHomeOnDisplayWithoutSysDecorations()995 public void testStartSecondaryHomeOnDisplayWithoutSysDecorations() { 996 // Create secondary displays. 997 final TestDisplayContent secondDisplay = 998 new TestDisplayContent.Builder(mAtm, 1000, 1500) 999 .setSystemDecorations(false).build(); 1000 1001 mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome", 1002 secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */); 1003 1004 verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any()); 1005 } 1006 1007 /** 1008 * Tests that when starting {@link ResolverActivity} for home, it should use the standard 1009 * activity type (in a new root task) so the order of back stack won't be broken. 1010 */ 1011 @Test testStartResolverActivityForHome()1012 public void testStartResolverActivityForHome() { 1013 final ActivityInfo info = new ActivityInfo(); 1014 info.applicationInfo = new ApplicationInfo(); 1015 info.applicationInfo.packageName = "android"; 1016 info.name = ResolverActivity.class.getName(); 1017 doReturn(info).when(mRootWindowContainer).resolveHomeActivity(anyInt(), any()); 1018 1019 mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY); 1020 final ActivityRecord resolverActivity = mRootWindowContainer.topRunningActivity(); 1021 1022 assertEquals(info, resolverActivity.info); 1023 assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getRootTask().getActivityType()); 1024 } 1025 1026 /** 1027 * Tests that secondary home should be selected if primary home not set. 1028 */ 1029 @Test testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet()1030 public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet() { 1031 // Setup: primary home not set. 1032 final Intent primaryHomeIntent = mAtm.getHomeIntent(); 1033 final ActivityInfo aInfoPrimary = new ActivityInfo(); 1034 aInfoPrimary.name = ResolverActivity.class.getName(); 1035 doReturn(aInfoPrimary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), 1036 refEq(primaryHomeIntent)); 1037 // Setup: set secondary home. 1038 mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */); 1039 1040 // Run the test. 1041 final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer 1042 .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); 1043 final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); 1044 assertEquals(aInfoSecondary.name, resolvedInfo.first.name); 1045 assertEquals(aInfoSecondary.applicationInfo.packageName, 1046 resolvedInfo.first.applicationInfo.packageName); 1047 } 1048 1049 /** 1050 * Tests that the default secondary home activity is always picked when it is in forced by 1051 * config_useSystemProvidedLauncherForSecondary. 1052 */ 1053 @Test testResolveSecondaryHomeActivityForced()1054 public void testResolveSecondaryHomeActivityForced() { 1055 // SetUp: set primary home. 1056 mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); 1057 // SetUp: set secondary home and force it. 1058 mockResolveHomeActivity(false /* primaryHome */, true /* forceSystemProvided */); 1059 final Intent secondaryHomeIntent = 1060 mAtm.getSecondaryHomeIntent(null /* preferredPackage */); 1061 final List<ResolveInfo> resolutions = new ArrayList<>(); 1062 final ResolveInfo resolveInfo = new ResolveInfo(); 1063 final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); 1064 resolveInfo.activityInfo = aInfoSecondary; 1065 resolutions.add(resolveInfo); 1066 doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), 1067 refEq(secondaryHomeIntent)); 1068 doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(), 1069 anyBoolean()); 1070 1071 // Run the test. 1072 final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer 1073 .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); 1074 assertEquals(aInfoSecondary.name, resolvedInfo.first.name); 1075 assertEquals(aInfoSecondary.applicationInfo.packageName, 1076 resolvedInfo.first.applicationInfo.packageName); 1077 } 1078 1079 /** 1080 * Tests that secondary home should be selected if primary home not support secondary displays 1081 * or there is no matched activity in the same package as selected primary home. 1082 */ 1083 @Test testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay()1084 public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay() { 1085 // Setup: there is no matched activity in the same package as selected primary home. 1086 mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); 1087 final List<ResolveInfo> resolutions = new ArrayList<>(); 1088 doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); 1089 // Setup: set secondary home. 1090 mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */); 1091 1092 // Run the test. 1093 final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer 1094 .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); 1095 final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); 1096 assertEquals(aInfoSecondary.name, resolvedInfo.first.name); 1097 assertEquals(aInfoSecondary.applicationInfo.packageName, 1098 resolvedInfo.first.applicationInfo.packageName); 1099 } 1100 /** 1101 * Tests that primary home activity should be selected if it already support secondary displays. 1102 */ 1103 @Test testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay()1104 public void testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay() { 1105 // SetUp: set primary home. 1106 mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); 1107 // SetUp: put primary home info on 2nd item 1108 final List<ResolveInfo> resolutions = new ArrayList<>(); 1109 final ResolveInfo infoFake1 = new ResolveInfo(); 1110 infoFake1.activityInfo = new ActivityInfo(); 1111 infoFake1.activityInfo.name = "fakeActivity1"; 1112 infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); 1113 infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; 1114 final ResolveInfo infoFake2 = new ResolveInfo(); 1115 final ActivityInfo aInfoPrimary = getFakeHomeActivityInfo(true /* primaryHome */); 1116 infoFake2.activityInfo = aInfoPrimary; 1117 resolutions.add(infoFake1); 1118 resolutions.add(infoFake2); 1119 doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); 1120 1121 doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(), 1122 anyBoolean()); 1123 1124 // Run the test. 1125 final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer 1126 .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); 1127 assertEquals(aInfoPrimary.name, resolvedInfo.first.name); 1128 assertEquals(aInfoPrimary.applicationInfo.packageName, 1129 resolvedInfo.first.applicationInfo.packageName); 1130 } 1131 1132 /** 1133 * Tests that the first one that matches should be selected if there are multiple activities. 1134 */ 1135 @Test testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay()1136 public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() { 1137 // SetUp: set primary home. 1138 mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); 1139 // Setup: prepare two eligible activity info. 1140 final List<ResolveInfo> resolutions = new ArrayList<>(); 1141 final ResolveInfo infoFake1 = new ResolveInfo(); 1142 infoFake1.activityInfo = new ActivityInfo(); 1143 infoFake1.activityInfo.name = "fakeActivity1"; 1144 infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); 1145 infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; 1146 final ResolveInfo infoFake2 = new ResolveInfo(); 1147 infoFake2.activityInfo = new ActivityInfo(); 1148 infoFake2.activityInfo.name = "fakeActivity2"; 1149 infoFake2.activityInfo.applicationInfo = new ApplicationInfo(); 1150 infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2"; 1151 resolutions.add(infoFake1); 1152 resolutions.add(infoFake2); 1153 doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); 1154 1155 doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(), 1156 anyBoolean()); 1157 1158 // Use the first one of matched activities in the same package as selected primary home. 1159 final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer 1160 .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); 1161 1162 assertEquals(infoFake1.activityInfo.applicationInfo.packageName, 1163 resolvedInfo.first.applicationInfo.packageName); 1164 assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name); 1165 } 1166 1167 @Test testGetLaunchRootTaskOnSecondaryTaskDisplayArea()1168 public void testGetLaunchRootTaskOnSecondaryTaskDisplayArea() { 1169 // Adding another TaskDisplayArea to the default display. 1170 final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); 1171 final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(display, 1172 mWm, "TDA", FEATURE_VENDOR_FIRST); 1173 display.addChild(taskDisplayArea, POSITION_BOTTOM); 1174 1175 // Making sure getting the root task from the preferred TDA and the preferred windowing mode 1176 LaunchParamsController.LaunchParams launchParams = 1177 new LaunchParamsController.LaunchParams(); 1178 launchParams.mPreferredTaskDisplayArea = taskDisplayArea; 1179 launchParams.mWindowingMode = WINDOWING_MODE_FREEFORM; 1180 Task root = mRootWindowContainer.getOrCreateRootTask(null /* r */, null /* options */, 1181 null /* candidateTask */, null /* sourceTask */, true /* onTop */, launchParams, 1182 0 /* launchParams */); 1183 assertEquals(taskDisplayArea, root.getTaskDisplayArea()); 1184 assertEquals(WINDOWING_MODE_FREEFORM, root.getWindowingMode()); 1185 1186 // Making sure still getting the root task from the preferred TDA when passing in a 1187 // launching activity. 1188 ActivityRecord r = new ActivityBuilder(mAtm).build(); 1189 root = mRootWindowContainer.getOrCreateRootTask(r, null /* options */, 1190 null /* candidateTask */, null /* sourceTask */, true /* onTop */, launchParams, 1191 0 /* launchParams */); 1192 assertEquals(taskDisplayArea, root.getTaskDisplayArea()); 1193 assertEquals(WINDOWING_MODE_FREEFORM, root.getWindowingMode()); 1194 } 1195 1196 @Test testGetOrCreateRootTaskOnDisplayWithCandidateRootTask()1197 public void testGetOrCreateRootTaskOnDisplayWithCandidateRootTask() { 1198 // Create a root task with an activity on secondary display. 1199 final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 300, 1200 600).build(); 1201 final Task task = new TaskBuilder(mSupervisor) 1202 .setDisplay(secondaryDisplay).build(); 1203 final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); 1204 1205 // Make sure the root task is valid and can be reused on default display. 1206 final Task rootTask = mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootTask( 1207 activity, null /* options */, task, null /* sourceTask */, null /* launchParams */, 1208 0 /* launchFlags */, ACTIVITY_TYPE_STANDARD, true /* onTop */); 1209 assertEquals(task, rootTask); 1210 } 1211 1212 @Test testSwitchUser_missingHomeRootTask()1213 public void testSwitchUser_missingHomeRootTask() { 1214 final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( 1215 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); 1216 doReturn(fullscreenTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask(); 1217 1218 final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); 1219 Task rootHomeTask = taskDisplayArea.getRootHomeTask(); 1220 if (rootHomeTask != null) { 1221 rootHomeTask.removeImmediately(); 1222 } 1223 assertNull(taskDisplayArea.getRootHomeTask()); 1224 1225 int currentUser = mRootWindowContainer.mCurrentUser; 1226 int otherUser = currentUser + 1; 1227 1228 mRootWindowContainer.switchUser(otherUser, null); 1229 1230 assertNotNull(taskDisplayArea.getRootHomeTask()); 1231 assertEquals(taskDisplayArea.getTopRootTask(), taskDisplayArea.getRootHomeTask()); 1232 } 1233 1234 @Test testLockAllProfileTasks()1235 public void testLockAllProfileTasks() { 1236 final int profileUid = UserHandle.PER_USER_RANGE + UserHandle.MIN_SECONDARY_USER_ID; 1237 final int profileUserId = UserHandle.getUserId(profileUid); 1238 // Create an activity belonging to the profile user. 1239 final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true) 1240 .setUid(profileUid).build(); 1241 final Task task = activity.getTask(); 1242 1243 // Create another activity belonging to current user on top. 1244 final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); 1245 topActivity.intent.setAction(Intent.ACTION_MAIN); 1246 1247 // Make sure the listeners will be notified for putting the task to locked state 1248 TaskChangeNotificationController controller = mAtm.getTaskChangeNotificationController(); 1249 spyOn(controller); 1250 mWm.mRoot.lockAllProfileTasks(profileUserId); 1251 verify(controller).notifyTaskProfileLocked(any(), eq(profileUserId)); 1252 1253 // Create the work lock activity on top of the task 1254 final ActivityRecord workLockActivity = new ActivityBuilder(mAtm).setTask(task).build(); 1255 workLockActivity.intent.setAction(ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER); 1256 doReturn(workLockActivity.mActivityComponent).when(mAtm).getSysUiServiceComponentLocked(); 1257 1258 // Make sure the listener won't be notified again. 1259 clearInvocations(controller); 1260 mWm.mRoot.lockAllProfileTasks(profileUserId); 1261 verify(controller, never()).notifyTaskProfileLocked(any(), anyInt()); 1262 } 1263 1264 /** 1265 * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity 1266 * info for test cases. 1267 * 1268 * @param primaryHome Indicate to use primary home intent as parameter, otherwise, use 1269 * secondary home intent. 1270 * @param forceSystemProvided Indicate to force using system provided home activity. 1271 */ mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided)1272 private void mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided) { 1273 ActivityInfo targetActivityInfo = getFakeHomeActivityInfo(primaryHome); 1274 Intent targetIntent; 1275 if (primaryHome) { 1276 targetIntent = mAtm.getHomeIntent(); 1277 } else { 1278 Resources resources = mContext.getResources(); 1279 spyOn(resources); 1280 doReturn(targetActivityInfo.applicationInfo.packageName).when(resources).getString( 1281 com.android.internal.R.string.config_secondaryHomePackage); 1282 doReturn(forceSystemProvided).when(resources).getBoolean( 1283 com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); 1284 targetIntent = mAtm.getSecondaryHomeIntent(null /* preferredPackage */); 1285 } 1286 doReturn(targetActivityInfo).when(mRootWindowContainer).resolveHomeActivity(anyInt(), 1287 refEq(targetIntent)); 1288 } 1289 1290 /** 1291 * Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent 1292 * activity info for test cases. 1293 */ mockResolveSecondaryHomeActivity()1294 private void mockResolveSecondaryHomeActivity() { 1295 final Intent secondaryHomeIntent = mAtm 1296 .getSecondaryHomeIntent(null /* preferredPackage */); 1297 final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false); 1298 doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer) 1299 .resolveSecondaryHomeActivity(anyInt(), any()); 1300 } 1301 getFakeHomeActivityInfo(boolean primaryHome)1302 private ActivityInfo getFakeHomeActivityInfo(boolean primaryHome) { 1303 final ActivityInfo aInfo = new ActivityInfo(); 1304 aInfo.name = primaryHome ? "fakeHomeActivity" : "fakeSecondaryHomeActivity"; 1305 aInfo.applicationInfo = new ApplicationInfo(); 1306 aInfo.applicationInfo.packageName = 1307 primaryHome ? "fakeHomePackage" : "fakeSecondaryHomePackage"; 1308 return aInfo; 1309 } 1310 } 1311 1312