• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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