1 /* 2 * Copyright (C) 2018 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.WindowConfiguration.ACTIVITY_TYPE_HOME; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; 24 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 25 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; 26 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; 27 import static android.view.Display.DEFAULT_DISPLAY; 28 import static android.view.Display.TYPE_VIRTUAL; 29 30 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; 33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; 34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; 35 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 36 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; 37 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 38 import static com.android.server.wm.ActivityDisplay.POSITION_TOP; 39 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; 40 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; 41 import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; 42 43 import static org.junit.Assert.assertEquals; 44 import static org.junit.Assert.assertFalse; 45 import static org.junit.Assert.assertNotNull; 46 import static org.junit.Assert.assertNull; 47 import static org.junit.Assert.assertTrue; 48 import static org.mockito.ArgumentMatchers.any; 49 import static org.mockito.ArgumentMatchers.anyBoolean; 50 import static org.mockito.ArgumentMatchers.anyInt; 51 import static org.mockito.ArgumentMatchers.contains; 52 import static org.mockito.ArgumentMatchers.eq; 53 import static org.mockito.ArgumentMatchers.refEq; 54 55 import android.app.ActivityOptions; 56 import android.content.ComponentName; 57 import android.content.Intent; 58 import android.content.pm.ActivityInfo; 59 import android.content.pm.ApplicationInfo; 60 import android.content.pm.ResolveInfo; 61 import android.content.res.Resources; 62 import android.graphics.Rect; 63 import android.platform.test.annotations.Presubmit; 64 import android.util.Pair; 65 import android.view.DisplayInfo; 66 67 import androidx.test.filters.MediumTest; 68 69 import com.android.internal.app.ResolverActivity; 70 import com.android.server.wm.ActivityStack.ActivityState; 71 72 import org.junit.Before; 73 import org.junit.Test; 74 75 import java.util.ArrayList; 76 import java.util.Arrays; 77 import java.util.List; 78 79 /** 80 * Tests for the {@link RootActivityContainer} class. 81 * 82 * Build/Install/Run: 83 * atest WmTests:RootActivityContainerTests 84 */ 85 @MediumTest 86 @Presubmit 87 public class RootActivityContainerTests extends ActivityTestsBase { 88 private ActivityStack mFullscreenStack; 89 90 @Before setUp()91 public void setUp() throws Exception { 92 mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack( 93 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); 94 } 95 96 /** 97 * This test ensures that we do not try to restore a task based off an invalid task id. We 98 * should expect {@code null} to be returned in this case. 99 */ 100 @Test testRestoringInvalidTask()101 public void testRestoringInvalidTask() { 102 ((TestActivityDisplay) mRootActivityContainer.getDefaultDisplay()).removeAllTasks(); 103 TaskRecord task = mRootActivityContainer.anyTaskForId(0 /*taskId*/, 104 MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */); 105 assertNull(task); 106 } 107 108 /** 109 * This test ensures that an existing task in the pinned stack is moved to the fullscreen 110 * activity stack when a new task is added. 111 */ 112 @Test testReplacingTaskInPinnedStack()113 public void testReplacingTaskInPinnedStack() { 114 final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) 115 .setStack(mFullscreenStack).build(); 116 final TaskRecord firstTask = firstActivity.getTaskRecord(); 117 118 final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true) 119 .setStack(mFullscreenStack).build(); 120 final TaskRecord secondTask = secondActivity.getTaskRecord(); 121 122 mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack"); 123 124 // Ensure full screen stack has both tasks. 125 ensureStackPlacement(mFullscreenStack, firstTask, secondTask); 126 127 // Move first activity to pinned stack. 128 final Rect sourceBounds = new Rect(); 129 mRootActivityContainer.moveActivityToPinnedStack(firstActivity, sourceBounds, 130 0f /*aspectRatio*/, "initialMove"); 131 132 final ActivityDisplay display = mFullscreenStack.getDisplay(); 133 ActivityStack pinnedStack = display.getPinnedStack(); 134 // Ensure a task has moved over. 135 ensureStackPlacement(pinnedStack, firstTask); 136 ensureStackPlacement(mFullscreenStack, secondTask); 137 138 // Move second activity to pinned stack. 139 mRootActivityContainer.moveActivityToPinnedStack(secondActivity, sourceBounds, 140 0f /*aspectRatio*/, "secondMove"); 141 142 // Need to get stacks again as a new instance might have been created. 143 pinnedStack = display.getPinnedStack(); 144 mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); 145 // Ensure stacks have swapped tasks. 146 ensureStackPlacement(pinnedStack, secondTask); 147 ensureStackPlacement(mFullscreenStack, firstTask); 148 } 149 ensureStackPlacement(ActivityStack stack, TaskRecord... tasks)150 private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) { 151 final ArrayList<TaskRecord> stackTasks = stack.getAllTasks(); 152 assertEquals("Expecting " + Arrays.deepToString(tasks) + " got " + stackTasks, 153 stackTasks.size(), tasks != null ? tasks.length : 0); 154 155 if (tasks == null) { 156 return; 157 } 158 159 for (TaskRecord task : tasks) { 160 assertTrue(stackTasks.contains(task)); 161 } 162 } 163 164 @Test testApplySleepTokens()165 public void testApplySleepTokens() { 166 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); 167 final KeyguardController keyguard = mSupervisor.getKeyguardController(); 168 final ActivityStack stack = mock(ActivityStack.class); 169 display.addChild(stack, 0 /* position */); 170 171 // Make sure we wake and resume in the case the display is turning on and the keyguard is 172 // not showing. 173 verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, 174 false /* displayShouldSleep */, true /* isFocusedStack */, 175 false /* keyguardShowing */, true /* expectWakeFromSleep */, 176 true /* expectResumeTopActivity */); 177 178 // Make sure we wake and don't resume when the display is turning on and the keyguard is 179 // showing. 180 verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, 181 false /* displayShouldSleep */, true /* isFocusedStack */, 182 true /* keyguardShowing */, true /* expectWakeFromSleep */, 183 false /* expectResumeTopActivity */); 184 185 // Make sure we wake and don't resume when the display is turning on and the keyguard is 186 // not showing as unfocused. 187 verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, 188 false /* displayShouldSleep */, false /* isFocusedStack */, 189 false /* keyguardShowing */, true /* expectWakeFromSleep */, 190 false /* expectResumeTopActivity */); 191 192 // Should not do anything if the display state hasn't changed. 193 verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/, 194 false /* displayShouldSleep */, true /* isFocusedStack */, 195 false /* keyguardShowing */, false /* expectWakeFromSleep */, 196 false /* expectResumeTopActivity */); 197 } 198 verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard, ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep, boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep, boolean expectResumeTopActivity)199 private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard, 200 ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep, 201 boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep, 202 boolean expectResumeTopActivity) { 203 reset(stack); 204 205 doReturn(displayShouldSleep).when(display).shouldSleep(); 206 doReturn(displaySleeping).when(display).isSleeping(); 207 doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt()); 208 209 doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay(); 210 doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack(); 211 mRootActivityContainer.applySleepTokens(true); 212 verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked(); 213 verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked( 214 null /* target */, null /* targetOptions */); 215 } 216 217 /** 218 * Verifies that removal of activity with task and stack is done correctly. 219 */ 220 @Test testRemovingStackOnAppCrash()221 public void testRemovingStackOnAppCrash() { 222 final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay(); 223 final int originalStackCount = defaultDisplay.getChildCount(); 224 final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack( 225 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 226 final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) 227 .setStack(stack).build(); 228 229 assertEquals(originalStackCount + 1, defaultDisplay.getChildCount()); 230 231 // Let's pretend that the app has crashed. 232 firstActivity.app.setThread(null); 233 mRootActivityContainer.finishTopCrashedActivities(firstActivity.app, "test"); 234 235 // Verify that the stack was removed. 236 assertEquals(originalStackCount, defaultDisplay.getChildCount()); 237 } 238 239 @Test testFocusability()240 public void testFocusability() { 241 final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack( 242 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); 243 final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true) 244 .setStack(stack).build(); 245 246 // Under split screen primary we should be focusable when not minimized 247 mRootActivityContainer.setDockedStackMinimized(false); 248 assertTrue(stack.isFocusable()); 249 assertTrue(activity.isFocusable()); 250 251 // Under split screen primary we should not be focusable when minimized 252 mRootActivityContainer.setDockedStackMinimized(true); 253 assertFalse(stack.isFocusable()); 254 assertFalse(activity.isFocusable()); 255 256 final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack( 257 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); 258 final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true) 259 .setStack(pinnedStack).build(); 260 261 // We should not be focusable when in pinned mode 262 assertFalse(pinnedStack.isFocusable()); 263 assertFalse(pinnedActivity.isFocusable()); 264 265 // Add flag forcing focusability. 266 pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE; 267 268 // We should not be focusable when in pinned mode 269 assertTrue(pinnedStack.isFocusable()); 270 assertTrue(pinnedActivity.isFocusable()); 271 272 // Without the overridding activity, stack should not be focusable. 273 pinnedStack.removeTask(pinnedActivity.getTaskRecord(), "testFocusability", 274 REMOVE_TASK_MODE_DESTROYING); 275 assertFalse(pinnedStack.isFocusable()); 276 } 277 278 /** 279 * Verify that split-screen primary stack will be chosen if activity is launched that targets 280 * split-screen secondary, but a matching existing instance is found on top of split-screen 281 * primary stack. 282 */ 283 @Test testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary()284 public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() { 285 // Create primary split-screen stack with a task and an activity. 286 final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay() 287 .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, 288 true /* onTop */); 289 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build(); 290 final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build(); 291 292 // Find a launch stack for the top activity in split-screen primary, while requesting 293 // split-screen secondary. 294 final ActivityOptions options = ActivityOptions.makeBasic(); 295 options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); 296 final ActivityStack result = 297 mRootActivityContainer.getLaunchStack(r, options, task, true /* onTop */); 298 299 // Assert that the primary stack is returned. 300 assertEquals(primaryStack, result); 301 } 302 303 /** 304 * Verify split-screen primary stack & task can resized by 305 * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect. 306 */ 307 @Test testResizeDockedStackForSplitScreenPrimary()308 public void testResizeDockedStackForSplitScreenPrimary() { 309 final Rect taskSize = new Rect(0, 0, 600, 600); 310 final Rect stackSize = new Rect(0, 0, 300, 300); 311 312 // Create primary split-screen stack with a task. 313 final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay() 314 .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, 315 true /* onTop */); 316 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build(); 317 318 // Resize dock stack. 319 mService.resizeDockedStack(stackSize, taskSize, null, null, null); 320 321 // Verify dock stack & its task bounds if is equal as resized result. 322 assertEquals(primaryStack.getBounds(), stackSize); 323 assertEquals(task.getBounds(), taskSize); 324 } 325 326 /** 327 * Verify that home stack would be moved to front when the top activity is Recents. 328 */ 329 @Test testFindTaskToMoveToFrontWhenRecentsOnTop()330 public void testFindTaskToMoveToFrontWhenRecentsOnTop() { 331 // Create stack/task on default display. 332 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); 333 final TestActivityStack targetStack = (TestActivityStack) new StackBuilder( 334 mRootActivityContainer).setOnTop(false).build(); 335 final TaskRecord targetTask = targetStack.getChildAt(0); 336 337 // Create Recents on top of the display. 338 final ActivityStack stack = new StackBuilder(mRootActivityContainer).setActivityType( 339 ACTIVITY_TYPE_RECENTS).build(); 340 341 final String reason = "findTaskToMoveToFront"; 342 mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, 343 false); 344 345 verify(display).moveHomeStackToFront(contains(reason)); 346 } 347 348 /** 349 * Verify that home stack won't be moved to front if the top activity on other display is 350 * Recents. 351 */ 352 @Test testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay()353 public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() { 354 // Create stack/task on default display. 355 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); 356 final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN, 357 ACTIVITY_TYPE_STANDARD, false /* onTop */); 358 final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build(); 359 360 // Create Recents on secondary display. 361 final TestActivityDisplay secondDisplay = addNewActivityDisplayAt( 362 ActivityDisplay.POSITION_TOP); 363 final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN, 364 ACTIVITY_TYPE_RECENTS, true /* onTop */); 365 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build(); 366 new ActivityBuilder(mService).setTask(task).build(); 367 368 final String reason = "findTaskToMoveToFront"; 369 mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, 370 false); 371 372 verify(display, never()).moveHomeStackToFront(contains(reason)); 373 } 374 375 /** 376 * Verify if a stack is not at the topmost position, it should be able to resume its activity if 377 * the stack is the top focused. 378 */ 379 @Test testResumeActivityWhenNonTopmostStackIsTopFocused()380 public void testResumeActivityWhenNonTopmostStackIsTopFocused() { 381 // Create a stack at bottom. 382 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); 383 final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN, 384 ACTIVITY_TYPE_STANDARD, false /* onTop */)); 385 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); 386 final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build(); 387 display.positionChildAtBottom(targetStack); 388 389 // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it 390 // is the current top focused stack. 391 assertFalse(targetStack.isTopStackOnDisplay()); 392 doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack(); 393 394 // Use the stack as target to resume. 395 mRootActivityContainer.resumeFocusedStacksTopActivities( 396 targetStack, activity, null /* targetOptions */); 397 398 // Verify the target stack should resume its activity. 399 verify(targetStack, times(1)).resumeTopActivityUncheckedLocked( 400 eq(activity), eq(null /* targetOptions */)); 401 } 402 403 /** 404 * Verify that home activity will be started on a display even if another display has a 405 * focusable activity. 406 */ 407 @Test testResumeFocusedStacksStartsHomeActivity_NoActivities()408 public void testResumeFocusedStacksStartsHomeActivity_NoActivities() { 409 mFullscreenStack.remove(); 410 mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY).getHomeStack().remove(); 411 mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY) 412 .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); 413 414 doReturn(true).when(mRootActivityContainer).resumeHomeActivity(any(), any(), anyInt()); 415 416 mService.setBooted(true); 417 418 // Trigger resume on all displays 419 mRootActivityContainer.resumeFocusedStacksTopActivities(); 420 421 // Verify that home activity was started on the default display 422 verify(mRootActivityContainer).resumeHomeActivity(any(), any(), eq(DEFAULT_DISPLAY)); 423 } 424 425 /** 426 * Verify that home activity will be started on a display even if another display has a 427 * focusable activity. 428 */ 429 @Test testResumeFocusedStacksStartsHomeActivity_ActivityOnSecondaryScreen()430 public void testResumeFocusedStacksStartsHomeActivity_ActivityOnSecondaryScreen() { 431 mFullscreenStack.remove(); 432 mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY).getHomeStack().remove(); 433 mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY) 434 .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); 435 436 // Create an activity on secondary display. 437 final TestActivityDisplay secondDisplay = addNewActivityDisplayAt( 438 ActivityDisplay.POSITION_TOP); 439 final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN, 440 ACTIVITY_TYPE_STANDARD, true /* onTop */); 441 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build(); 442 new ActivityBuilder(mService).setTask(task).build(); 443 444 doReturn(true).when(mRootActivityContainer).resumeHomeActivity(any(), any(), anyInt()); 445 446 mService.setBooted(true); 447 448 // Trigger resume on all displays 449 mRootActivityContainer.resumeFocusedStacksTopActivities(); 450 451 // Verify that home activity was started on the default display 452 verify(mRootActivityContainer).resumeHomeActivity(any(), any(), eq(DEFAULT_DISPLAY)); 453 } 454 455 /** 456 * Verify that a lingering transition is being executed in case the activity to be resumed is 457 * already resumed 458 */ 459 @Test testResumeActivityLingeringTransition()460 public void testResumeActivityLingeringTransition() { 461 // Create a stack at top. 462 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); 463 final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN, 464 ACTIVITY_TYPE_STANDARD, false /* onTop */)); 465 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); 466 final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build(); 467 activity.setState(ActivityState.RESUMED, "test"); 468 469 // Assume the stack is at the topmost position 470 assertTrue(targetStack.isTopStackOnDisplay()); 471 472 // Use the stack as target to resume. 473 mRootActivityContainer.resumeFocusedStacksTopActivities(); 474 475 // Verify the lingering app transition is being executed because it's already resumed 476 verify(targetStack, times(1)).executeAppTransition(any()); 477 } 478 479 @Test testResumeActivityLingeringTransition_notExecuted()480 public void testResumeActivityLingeringTransition_notExecuted() { 481 // Create a stack at bottom. 482 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); 483 final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN, 484 ACTIVITY_TYPE_STANDARD, false /* onTop */)); 485 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); 486 final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build(); 487 activity.setState(ActivityState.RESUMED, "test"); 488 display.positionChildAtBottom(targetStack); 489 490 // Assume the stack is at the topmost position 491 assertFalse(targetStack.isTopStackOnDisplay()); 492 doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack(); 493 494 // Use the stack as target to resume. 495 mRootActivityContainer.resumeFocusedStacksTopActivities(); 496 497 // Verify the lingering app transition is being executed because it's already resumed 498 verify(targetStack, never()).executeAppTransition(any()); 499 } 500 501 /** 502 * Tests that home activities can be started on the displays that supports system decorations. 503 */ 504 @Test testStartHomeOnAllDisplays()505 public void testStartHomeOnAllDisplays() { 506 mockResolveHomeActivity(); 507 508 // Create secondary displays. 509 final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); 510 mRootActivityContainer.addChild(secondDisplay, POSITION_TOP); 511 doReturn(true).when(secondDisplay).supportsSystemDecorations(); 512 513 // Create mock tasks and other necessary mocks. 514 mockTaskRecordFactory(); 515 doReturn(true).when(mRootActivityContainer) 516 .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean()); 517 doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay( 518 any(), anyInt(), anyBoolean()); 519 520 mRootActivityContainer.startHomeOnAllDisplays(0, "testStartHome"); 521 522 assertTrue(mRootActivityContainer.getDefaultDisplay().getTopStack().isActivityTypeHome()); 523 assertNotNull(secondDisplay.getTopStack()); 524 assertTrue(secondDisplay.getTopStack().isActivityTypeHome()); 525 } 526 527 /** 528 * Tests that home activities won't be started before booting when display added. 529 */ 530 @Test testNotStartHomeBeforeBoot()531 public void testNotStartHomeBeforeBoot() { 532 final int displayId = 1; 533 final boolean isBooting = mService.mAmInternal.isBooting(); 534 final boolean isBooted = mService.mAmInternal.isBooted(); 535 try { 536 mService.mAmInternal.setBooting(false); 537 mService.mAmInternal.setBooted(false); 538 mRootActivityContainer.onDisplayAdded(displayId); 539 verify(mRootActivityContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt()); 540 } finally { 541 mService.mAmInternal.setBooting(isBooting); 542 mService.mAmInternal.setBooted(isBooted); 543 } 544 } 545 546 /** 547 * Tests whether home can be started if being instrumented. 548 */ 549 @Test testCanStartHomeWhenInstrumented()550 public void testCanStartHomeWhenInstrumented() { 551 final ActivityInfo info = new ActivityInfo(); 552 info.applicationInfo = new ApplicationInfo(); 553 final WindowProcessController app = mock(WindowProcessController.class); 554 doReturn(app).when(mService).getProcessController(any(), anyInt()); 555 556 // Can not start home if we don't want to start home while home is being instrumented. 557 doReturn(true).when(app).isInstrumenting(); 558 assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, 559 false /* allowInstrumenting*/)); 560 561 // Can start home for other cases. 562 assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, 563 true /* allowInstrumenting*/)); 564 565 doReturn(false).when(app).isInstrumenting(); 566 assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, 567 false /* allowInstrumenting*/)); 568 assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, 569 true /* allowInstrumenting*/)); 570 } 571 572 /** 573 * Tests that secondary home activity should not be resolved if device is still locked. 574 */ 575 @Test testStartSecondaryHomeOnDisplayWithUserKeyLocked()576 public void testStartSecondaryHomeOnDisplayWithUserKeyLocked() { 577 // Create secondary displays. 578 final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); 579 mRootActivityContainer.addChild(secondDisplay, POSITION_TOP); 580 581 doReturn(true).when(secondDisplay).supportsSystemDecorations(); 582 // Use invalid user id to let StorageManager.isUserKeyUnlocked() return false. 583 final int currentUser = mRootActivityContainer.mCurrentUser; 584 mRootActivityContainer.mCurrentUser = -1; 585 586 mRootActivityContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome", 587 secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */); 588 589 try { 590 verify(mRootActivityContainer, never()).resolveSecondaryHomeActivity(anyInt(), 591 anyInt()); 592 } finally { 593 mRootActivityContainer.mCurrentUser = currentUser; 594 } 595 } 596 597 /** 598 * Tests that secondary home activity should not be resolved if display does not support system 599 * decorations. 600 */ 601 @Test testStartSecondaryHomeOnDisplayWithoutSysDecorations()602 public void testStartSecondaryHomeOnDisplayWithoutSysDecorations() { 603 // Create secondary displays. 604 final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); 605 mRootActivityContainer.addChild(secondDisplay, POSITION_TOP); 606 doReturn(false).when(secondDisplay).supportsSystemDecorations(); 607 608 mRootActivityContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome", 609 secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */); 610 611 verify(mRootActivityContainer, never()).resolveSecondaryHomeActivity(anyInt(), anyInt()); 612 } 613 614 /** 615 * Tests that when starting {@link #ResolverActivity} for home, it should use the standard 616 * activity type (in a new stack) so the order of back stack won't be broken. 617 */ 618 @Test testStartResolverActivityForHome()619 public void testStartResolverActivityForHome() { 620 final ActivityInfo info = new ActivityInfo(); 621 info.applicationInfo = new ApplicationInfo(); 622 info.applicationInfo.packageName = "android"; 623 info.name = ResolverActivity.class.getName(); 624 doReturn(info).when(mRootActivityContainer).resolveHomeActivity(anyInt(), any()); 625 mockTaskRecordFactory(); 626 627 mRootActivityContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY); 628 final ActivityRecord resolverActivity = mRootActivityContainer.topRunningActivity(); 629 630 assertEquals(info, resolverActivity.info); 631 assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getActivityStack().getActivityType()); 632 } 633 634 /** 635 * Tests that secondary home should be selected if default home not set. 636 */ 637 @Test testResolveSecondaryHomeActivityWhenDefaultHomeNotSet()638 public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSet() { 639 final Intent defaultHomeIntent = mService.getHomeIntent(); 640 final ActivityInfo aInfoDefault = new ActivityInfo(); 641 aInfoDefault.name = ResolverActivity.class.getName(); 642 doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), 643 refEq(defaultHomeIntent)); 644 645 final String secondaryHomeComponent = mService.mContext.getResources().getString( 646 com.android.internal.R.string.config_secondaryHomeComponent); 647 final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent); 648 final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); 649 final ActivityInfo aInfoSecondary = new ActivityInfo(); 650 aInfoSecondary.name = comp.getClassName(); 651 doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(), 652 refEq(secondaryHomeIntent)); 653 654 // Should fallback to secondary home if default home not set. 655 final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer 656 .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); 657 658 assertEquals(comp.getClassName(), resolvedInfo.first.name); 659 } 660 661 /** 662 * Tests that the default secondary home activity is always picked when it is in forced by 663 * config_useSystemProvidedLauncherForSecondary. 664 */ 665 @Test testResolveSecondaryHomeActivityForced()666 public void testResolveSecondaryHomeActivityForced() throws Exception { 667 Resources resources = mContext.getResources(); 668 spyOn(resources); 669 try { 670 // setUp: set secondary launcher and force it. 671 final String defaultSecondaryHome = 672 "com.android.test/com.android.test.TestDefaultSecondaryHome"; 673 final ComponentName secondaryComp = ComponentName.unflattenFromString( 674 defaultSecondaryHome); 675 doReturn(defaultSecondaryHome).when(resources).getString( 676 com.android.internal.R.string.config_secondaryHomeComponent); 677 doReturn(true).when(resources).getBoolean( 678 com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); 679 final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); 680 assertEquals(secondaryComp, secondaryHomeIntent.getComponent()); 681 final ActivityInfo aInfoSecondary = new ActivityInfo(); 682 aInfoSecondary.name = secondaryComp.getClassName(); 683 aInfoSecondary.applicationInfo = new ApplicationInfo(); 684 aInfoSecondary.applicationInfo.packageName = secondaryComp.getPackageName(); 685 doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(), 686 refEq(secondaryHomeIntent)); 687 final Intent homeIntent = mService.getHomeIntent(); 688 final ActivityInfo aInfoDefault = new ActivityInfo(); 689 aInfoDefault.name = "fakeHomeActivity"; 690 aInfoDefault.applicationInfo = new ApplicationInfo(); 691 aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; 692 doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), 693 refEq(homeIntent)); 694 // Let resolveActivities call to validate both main launcher and second launcher so that 695 // resolveActivities call does not work as enabler for secondary. 696 final List<ResolveInfo> resolutions1 = new ArrayList<>(); 697 final ResolveInfo resolveInfo1 = new ResolveInfo(); 698 resolveInfo1.activityInfo = new ActivityInfo(); 699 resolveInfo1.activityInfo.name = aInfoDefault.name; 700 resolveInfo1.activityInfo.applicationInfo = aInfoDefault.applicationInfo; 701 resolutions1.add(resolveInfo1); 702 doReturn(resolutions1).when(mRootActivityContainer).resolveActivities(anyInt(), 703 refEq(homeIntent)); 704 final List<ResolveInfo> resolutions2 = new ArrayList<>(); 705 final ResolveInfo resolveInfo2 = new ResolveInfo(); 706 resolveInfo2.activityInfo = new ActivityInfo(); 707 resolveInfo2.activityInfo.name = aInfoSecondary.name; 708 resolveInfo2.activityInfo.applicationInfo = aInfoSecondary.applicationInfo; 709 resolutions2.add(resolveInfo2); 710 doReturn(resolutions2).when(mRootActivityContainer).resolveActivities(anyInt(), 711 refEq(secondaryHomeIntent)); 712 doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay( 713 any(), anyInt(), anyBoolean()); 714 715 // Run the test 716 final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer 717 .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); 718 assertEquals(secondaryComp.getClassName(), resolvedInfo.first.name); 719 assertEquals(secondaryComp.getPackageName(), 720 resolvedInfo.first.applicationInfo.packageName); 721 assertEquals(aInfoSecondary.name, resolvedInfo.first.name); 722 } finally { 723 // tearDown 724 reset(resources); 725 } 726 } 727 728 /** 729 * Tests that secondary home should be selected if default home not support secondary displays 730 * or there is no matched activity in the same package as selected default home. 731 */ 732 @Test testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay()733 public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() { 734 mockResolveHomeActivity(); 735 736 final List<ResolveInfo> resolutions = new ArrayList<>(); 737 doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any()); 738 739 final String secondaryHomeComponent = mService.mContext.getResources().getString( 740 com.android.internal.R.string.config_secondaryHomeComponent); 741 final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent); 742 final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); 743 final ActivityInfo aInfoSecondary = new ActivityInfo(); 744 aInfoSecondary.name = comp.getClassName(); 745 doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(), 746 refEq(secondaryHomeIntent)); 747 748 // Should fallback to secondary home if selected default home not support secondary displays 749 // or there is no matched activity in the same package as selected default home. 750 final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer 751 .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); 752 753 assertEquals(comp.getClassName(), resolvedInfo.first.name); 754 } 755 756 /** 757 * Tests that default home activity should be selected if it already support secondary displays. 758 */ 759 @Test testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay()760 public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() { 761 final ActivityInfo aInfoDefault = mockResolveHomeActivity(); 762 763 final List<ResolveInfo> resolutions = new ArrayList<>(); 764 final ResolveInfo infoFake1 = new ResolveInfo(); 765 infoFake1.activityInfo = new ActivityInfo(); 766 infoFake1.activityInfo.name = "fakeActivity1"; 767 infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); 768 infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; 769 final ResolveInfo infoFake2 = new ResolveInfo(); 770 infoFake2.activityInfo = aInfoDefault; 771 resolutions.add(infoFake1); 772 resolutions.add(infoFake2); 773 doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any()); 774 775 doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay( 776 any(), anyInt(), anyBoolean()); 777 778 // Use default home activity if it support secondary displays. 779 final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer 780 .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); 781 782 assertEquals(aInfoDefault.applicationInfo.packageName, 783 resolvedInfo.first.applicationInfo.packageName); 784 assertEquals(aInfoDefault.name, resolvedInfo.first.name); 785 } 786 787 /** 788 * Tests that the first one that matches should be selected if there are multiple activities. 789 */ 790 @Test testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay()791 public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() { 792 mockResolveHomeActivity(); 793 794 final List<ResolveInfo> resolutions = new ArrayList<>(); 795 final ResolveInfo infoFake1 = new ResolveInfo(); 796 infoFake1.activityInfo = new ActivityInfo(); 797 infoFake1.activityInfo.name = "fakeActivity1"; 798 infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); 799 infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; 800 final ResolveInfo infoFake2 = new ResolveInfo(); 801 infoFake2.activityInfo = new ActivityInfo(); 802 infoFake2.activityInfo.name = "fakeActivity2"; 803 infoFake2.activityInfo.applicationInfo = new ApplicationInfo(); 804 infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2"; 805 resolutions.add(infoFake1); 806 resolutions.add(infoFake2); 807 doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any()); 808 809 doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay( 810 any(), anyInt(), anyBoolean()); 811 812 // Use the first one of matched activities in the same package as selected default home. 813 final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer 814 .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); 815 816 assertEquals(infoFake1.activityInfo.applicationInfo.packageName, 817 resolvedInfo.first.applicationInfo.packageName); 818 assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name); 819 } 820 821 /** 822 * Test that {@link RootActivityContainer#getLaunchStack} with the real caller id will get the 823 * expected stack when requesting the activity launch on the secondary display. 824 */ 825 @Test testGetLaunchStackWithRealCallerId()826 public void testGetLaunchStackWithRealCallerId() { 827 // Create a non-system owned virtual display. 828 final DisplayInfo info = new DisplayInfo(); 829 mSupervisor.mService.mContext.getDisplay().getDisplayInfo(info); 830 info.type = TYPE_VIRTUAL; 831 info.ownerUid = 100; 832 final TestActivityDisplay secondaryDisplay = createNewActivityDisplay(info); 833 mRootActivityContainer.addChild(secondaryDisplay, POSITION_TOP); 834 835 // Create an activity with specify the original launch pid / uid. 836 final ActivityRecord r = new ActivityBuilder(mService).setLaunchedFromPid(200) 837 .setLaunchedFromUid(200).build(); 838 839 // Simulate ActivityStarter to find a launch stack for requesting the activity to launch 840 // on the secondary display with realCallerId. 841 final ActivityOptions options = ActivityOptions.makeBasic(); 842 options.setLaunchDisplayId(secondaryDisplay.mDisplayId); 843 options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); 844 doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId, 845 300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info); 846 final ActivityStack result = mRootActivityContainer.getLaunchStack(r, options, 847 null /* task */, true /* onTop */, null, 300 /* test realCallerPid */, 848 300 /* test realCallerUid */); 849 850 // Assert that the stack is returned as expected. 851 assertNotNull(result); 852 assertEquals("The display ID of the stack should same as secondary display ", 853 secondaryDisplay.mDisplayId, result.mDisplayId); 854 } 855 856 /** 857 * Mock {@link RootActivityContainerTests#resolveHomeActivity} for returning consistent activity 858 * info for test cases (the original implementation will resolve from the real package manager). 859 */ mockResolveHomeActivity()860 private ActivityInfo mockResolveHomeActivity() { 861 final Intent homeIntent = mService.getHomeIntent(); 862 final ActivityInfo aInfoDefault = new ActivityInfo(); 863 aInfoDefault.name = "fakeHomeActivity"; 864 aInfoDefault.applicationInfo = new ApplicationInfo(); 865 aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; 866 doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), 867 refEq(homeIntent)); 868 return aInfoDefault; 869 } 870 } 871