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