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