blob: 539a79ccd78309a2530069b1a30eea04e72cf214 [file] [log] [blame]
Wale Ogunwaled32da472018-11-16 07:19:28 -08001/*
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
17package com.android.server.wm;
18
Andrii Kulian3ff5a0b2019-05-16 13:18:04 -070019import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
Wale Ogunwaled32da472018-11-16 07:19:28 -080020import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
21import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
22import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
23import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
24import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
25import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
26import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
Wale Ogunwaled32da472018-11-16 07:19:28 -080027import static android.view.Display.DEFAULT_DISPLAY;
Chilun2ef71f72018-11-16 17:57:15 +080028
Wale Ogunwaled32da472018-11-16 07:19:28 -080029import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
30import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
31import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
32import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
33import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
Keun young Park340546a2019-05-06 16:27:44 -070034import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
Wale Ogunwaled32da472018-11-16 07:19:28 -080035import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
36import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
37import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
38import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
Andrii Kulian3ff5a0b2019-05-16 13:18:04 -070039import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
Wale Ogunwaled32da472018-11-16 07:19:28 -080040import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
Chilun2ef71f72018-11-16 17:57:15 +080041
Wale Ogunwaled32da472018-11-16 07:19:28 -080042import static org.junit.Assert.assertEquals;
43import static org.junit.Assert.assertFalse;
44import static org.junit.Assert.assertNotNull;
45import static org.junit.Assert.assertNull;
46import static org.junit.Assert.assertTrue;
47import static org.mockito.ArgumentMatchers.any;
48import static org.mockito.ArgumentMatchers.anyBoolean;
49import static org.mockito.ArgumentMatchers.anyInt;
50import static org.mockito.ArgumentMatchers.contains;
51import static org.mockito.ArgumentMatchers.eq;
Chilun2ef71f72018-11-16 17:57:15 +080052import static org.mockito.ArgumentMatchers.refEq;
Wale Ogunwaled32da472018-11-16 07:19:28 -080053
54import android.app.ActivityOptions;
Chilun2ef71f72018-11-16 17:57:15 +080055import android.content.ComponentName;
56import android.content.Intent;
Wale Ogunwaled32da472018-11-16 07:19:28 -080057import android.content.pm.ActivityInfo;
58import android.content.pm.ApplicationInfo;
Chilun2ef71f72018-11-16 17:57:15 +080059import android.content.pm.ResolveInfo;
Keun young Park340546a2019-05-06 16:27:44 -070060import android.content.res.Resources;
Wale Ogunwaled32da472018-11-16 07:19:28 -080061import android.graphics.Rect;
Wale Ogunwaled32da472018-11-16 07:19:28 -080062import android.platform.test.annotations.Presubmit;
Chilun2ef71f72018-11-16 17:57:15 +080063import android.util.Pair;
64
Wale Ogunwaled32da472018-11-16 07:19:28 -080065import androidx.test.filters.MediumTest;
Chilun2ef71f72018-11-16 17:57:15 +080066
67import com.android.internal.app.ResolverActivity;
Jorim Jaggia5cf6802019-04-26 19:43:11 +020068import com.android.server.wm.ActivityStack.ActivityState;
Chilun2ef71f72018-11-16 17:57:15 +080069
Wale Ogunwaled32da472018-11-16 07:19:28 -080070import org.junit.Before;
71import org.junit.Test;
72
73import java.util.ArrayList;
Garfield Tan26835f02019-02-07 14:38:38 -080074import java.util.Arrays;
Chilun2ef71f72018-11-16 17:57:15 +080075import java.util.List;
Wale Ogunwaled32da472018-11-16 07:19:28 -080076
77/**
Riddle Hsuff9e8282019-04-24 23:55:11 +080078 * Tests for the {@link RootActivityContainer} class.
Wale Ogunwaled32da472018-11-16 07:19:28 -080079 *
80 * Build/Install/Run:
Riddle Hsuff9e8282019-04-24 23:55:11 +080081 * atest WmTests:RootActivityContainerTests
Wale Ogunwaled32da472018-11-16 07:19:28 -080082 */
83@MediumTest
84@Presubmit
85public class RootActivityContainerTests extends ActivityTestsBase {
86 private ActivityStack mFullscreenStack;
87
88 @Before
89 public void setUp() throws Exception {
Wale Ogunwaled32da472018-11-16 07:19:28 -080090 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
99 public void testRestoringInvalidTask() {
Wale Ogunwale8a1860a2019-06-05 08:57:19 -0700100 mRootActivityContainer.getDefaultDisplay().removeAllTasks();
Wale Ogunwaled32da472018-11-16 07:19:28 -0800101 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
111 public void testReplacingTaskInPinnedStack() {
112 final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
113 .setStack(mFullscreenStack).build();
Wale Ogunwale8b19de92018-11-29 19:58:26 -0800114 final TaskRecord firstTask = firstActivity.getTaskRecord();
Wale Ogunwaled32da472018-11-16 07:19:28 -0800115
116 final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
117 .setStack(mFullscreenStack).build();
Wale Ogunwale8b19de92018-11-29 19:58:26 -0800118 final TaskRecord secondTask = secondActivity.getTaskRecord();
Wale Ogunwaled32da472018-11-16 07:19:28 -0800119
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
148 private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
149 final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
Garfield Tan26835f02019-02-07 14:38:38 -0800150 assertEquals("Expecting " + Arrays.deepToString(tasks) + " got " + stackTasks,
151 stackTasks.size(), tasks != null ? tasks.length : 0);
Wale Ogunwaled32da472018-11-16 07:19:28 -0800152
153 if (tasks == null) {
154 return;
155 }
156
157 for (TaskRecord task : tasks) {
158 assertTrue(stackTasks.contains(task));
159 }
160 }
161
162 @Test
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
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
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
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.
Wale Ogunwale8b19de92018-11-29 19:58:26 -0800271 pinnedStack.removeTask(pinnedActivity.getTaskRecord(), "testFocusability",
Wale Ogunwaled32da472018-11-16 07:19:28 -0800272 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
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
306 public void testResizeDockedStackForSplitScreenPrimary() {
Wale Ogunwale8a1860a2019-06-05 08:57:19 -0700307 final Rect taskSize = new Rect(0, 0, 1000, 1000);
Wale Ogunwaled32da472018-11-16 07:19:28 -0800308 final Rect stackSize = new Rect(0, 0, 300, 300);
309
310 // Create primary split-screen stack with a task.
Wale Ogunwale8a1860a2019-06-05 08:57:19 -0700311 final ActivityStack primaryStack = new StackBuilder(mRootActivityContainer)
312 .setActivityType(ACTIVITY_TYPE_STANDARD)
313 .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
314 .setOnTop(true)
315 .build();
316 final TaskRecord task = primaryStack.topTask();
Wale Ogunwaled32da472018-11-16 07:19:28 -0800317
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.
Wale Ogunwale8a1860a2019-06-05 08:57:19 -0700322 assertEquals(stackSize, primaryStack.getBounds());
323 assertEquals(taskSize, task.getBounds());
Wale Ogunwaled32da472018-11-16 07:19:28 -0800324 }
325
326 /**
327 * Verify that home stack would be moved to front when the top activity is Recents.
328 */
329 @Test
330 public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
331 // Create stack/task on default display.
332 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
Wale Ogunwale8a1860a2019-06-05 08:57:19 -0700333 final ActivityStack targetStack = new StackBuilder(mRootActivityContainer)
334 .setOnTop(false)
335 .build();
Wale Ogunwaled32da472018-11-16 07:19:28 -0800336 final TaskRecord targetTask = targetStack.getChildAt(0);
337
338 // Create Recents on top of the display.
339 final ActivityStack stack = new StackBuilder(mRootActivityContainer).setActivityType(
340 ACTIVITY_TYPE_RECENTS).build();
341
342 final String reason = "findTaskToMoveToFront";
343 mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
344 false);
345
346 verify(display).moveHomeStackToFront(contains(reason));
347 }
348
349 /**
350 * Verify that home stack won't be moved to front if the top activity on other display is
351 * Recents.
352 */
353 @Test
354 public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
355 // Create stack/task on default display.
356 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
357 final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
358 ACTIVITY_TYPE_STANDARD, false /* onTop */);
359 final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
360
361 // Create Recents on secondary display.
362 final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
363 ActivityDisplay.POSITION_TOP);
364 final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
365 ACTIVITY_TYPE_RECENTS, true /* onTop */);
366 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
367 new ActivityBuilder(mService).setTask(task).build();
368
369 final String reason = "findTaskToMoveToFront";
370 mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
371 false);
372
373 verify(display, never()).moveHomeStackToFront(contains(reason));
374 }
375
376 /**
377 * Verify if a stack is not at the topmost position, it should be able to resume its activity if
378 * the stack is the top focused.
379 */
380 @Test
381 public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
382 // Create a stack at bottom.
383 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
384 final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
385 ACTIVITY_TYPE_STANDARD, false /* onTop */));
386 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
387 final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
388 display.positionChildAtBottom(targetStack);
389
390 // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
391 // is the current top focused stack.
392 assertFalse(targetStack.isTopStackOnDisplay());
393 doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack();
394
395 // Use the stack as target to resume.
396 mRootActivityContainer.resumeFocusedStacksTopActivities(
397 targetStack, activity, null /* targetOptions */);
398
399 // Verify the target stack should resume its activity.
400 verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
401 eq(activity), eq(null /* targetOptions */));
402 }
403
404 /**
Andrii Kulian3ff5a0b2019-05-16 13:18:04 -0700405 * Verify that home activity will be started on a display even if another display has a
406 * focusable activity.
407 */
408 @Test
409 public void testResumeFocusedStacksStartsHomeActivity_NoActivities() {
410 mFullscreenStack.remove();
411 mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY).getHomeStack().remove();
412 mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY)
413 .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
414
415 doReturn(true).when(mRootActivityContainer).resumeHomeActivity(any(), any(), anyInt());
416
417 mService.setBooted(true);
418
419 // Trigger resume on all displays
420 mRootActivityContainer.resumeFocusedStacksTopActivities();
421
422 // Verify that home activity was started on the default display
423 verify(mRootActivityContainer).resumeHomeActivity(any(), any(), eq(DEFAULT_DISPLAY));
424 }
425
426 /**
427 * Verify that home activity will be started on a display even if another display has a
428 * focusable activity.
429 */
430 @Test
431 public void testResumeFocusedStacksStartsHomeActivity_ActivityOnSecondaryScreen() {
432 mFullscreenStack.remove();
433 mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY).getHomeStack().remove();
434 mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY)
435 .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
436
437 // Create an activity on secondary display.
438 final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
439 ActivityDisplay.POSITION_TOP);
440 final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
441 ACTIVITY_TYPE_STANDARD, true /* onTop */);
442 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
443 new ActivityBuilder(mService).setTask(task).build();
444
445 doReturn(true).when(mRootActivityContainer).resumeHomeActivity(any(), any(), anyInt());
446
447 mService.setBooted(true);
448
449 // Trigger resume on all displays
450 mRootActivityContainer.resumeFocusedStacksTopActivities();
451
452 // Verify that home activity was started on the default display
453 verify(mRootActivityContainer).resumeHomeActivity(any(), any(), eq(DEFAULT_DISPLAY));
454 }
455
456 /**
Jorim Jaggia5cf6802019-04-26 19:43:11 +0200457 * Verify that a lingering transition is being executed in case the activity to be resumed is
458 * already resumed
459 */
460 @Test
461 public void testResumeActivityLingeringTransition() {
462 // Create a stack at top.
463 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
464 final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
465 ACTIVITY_TYPE_STANDARD, false /* onTop */));
466 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
467 final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
468 activity.setState(ActivityState.RESUMED, "test");
469
470 // Assume the stack is at the topmost position
471 assertTrue(targetStack.isTopStackOnDisplay());
472
473 // Use the stack as target to resume.
474 mRootActivityContainer.resumeFocusedStacksTopActivities();
475
476 // Verify the lingering app transition is being executed because it's already resumed
477 verify(targetStack, times(1)).executeAppTransition(any());
478 }
479
480 @Test
481 public void testResumeActivityLingeringTransition_notExecuted() {
482 // Create a stack at bottom.
483 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
484 final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
485 ACTIVITY_TYPE_STANDARD, false /* onTop */));
486 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
487 final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
488 activity.setState(ActivityState.RESUMED, "test");
489 display.positionChildAtBottom(targetStack);
490
491 // Assume the stack is at the topmost position
492 assertFalse(targetStack.isTopStackOnDisplay());
493 doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack();
494
495 // Use the stack as target to resume.
496 mRootActivityContainer.resumeFocusedStacksTopActivities();
497
498 // Verify the lingering app transition is being executed because it's already resumed
499 verify(targetStack, never()).executeAppTransition(any());
500 }
501
502 /**
Wale Ogunwaled32da472018-11-16 07:19:28 -0800503 * Tests that home activities can be started on the displays that supports system decorations.
504 */
Chilun2afb94e2018-12-25 20:42:45 +0800505 @Test
506 public void testStartHomeOnAllDisplays() {
Riddle Hsuff9e8282019-04-24 23:55:11 +0800507 mockResolveHomeActivity();
Chilun59954982019-05-23 14:50:48 +0800508 mockResolveSecondaryHomeActivity();
Riddle Hsuff9e8282019-04-24 23:55:11 +0800509
Wale Ogunwaled32da472018-11-16 07:19:28 -0800510 // Create secondary displays.
Wale Ogunwale8a1860a2019-06-05 08:57:19 -0700511 final TestActivityDisplay secondDisplay = createNewActivityDisplay();
Wale Ogunwaled32da472018-11-16 07:19:28 -0800512 mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
513 doReturn(true).when(secondDisplay).supportsSystemDecorations();
514
Wale Ogunwaled32da472018-11-16 07:19:28 -0800515 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
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
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 }
Chilun2ef71f72018-11-16 17:57:15 +0800571
572 /**
Chilun85ebc0d2019-04-15 16:00:53 +0800573 * Tests that secondary home activity should not be resolved if device is still locked.
574 */
575 @Test
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
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 /**
Riddle Hsuff9e8282019-04-24 23:55:11 +0800615 * 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
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());
Riddle Hsuff9e8282019-04-24 23:55:11 +0800625
626 mRootActivityContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY);
627 final ActivityRecord resolverActivity = mRootActivityContainer.topRunningActivity();
628
629 assertEquals(info, resolverActivity.info);
630 assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getActivityStack().getActivityType());
631 }
632
633 /**
Chilun2ef71f72018-11-16 17:57:15 +0800634 * Tests that secondary home should be selected if default home not set.
635 */
636 @Test
637 public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSet() {
638 final Intent defaultHomeIntent = mService.getHomeIntent();
639 final ActivityInfo aInfoDefault = new ActivityInfo();
640 aInfoDefault.name = ResolverActivity.class.getName();
641 doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
642 refEq(defaultHomeIntent));
643
644 final String secondaryHomeComponent = mService.mContext.getResources().getString(
645 com.android.internal.R.string.config_secondaryHomeComponent);
646 final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
647 final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
648 final ActivityInfo aInfoSecondary = new ActivityInfo();
649 aInfoSecondary.name = comp.getClassName();
650 doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
651 refEq(secondaryHomeIntent));
652
653 // Should fallback to secondary home if default home not set.
654 final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
655 .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
656
657 assertEquals(comp.getClassName(), resolvedInfo.first.name);
658 }
659
660 /**
Keun young Park340546a2019-05-06 16:27:44 -0700661 * Tests that the default secondary home activity is always picked when it is in forced by
662 * config_useSystemProvidedLauncherForSecondary.
663 */
664 @Test
665 public void testResolveSecondaryHomeActivityForced() throws Exception {
666 Resources resources = mContext.getResources();
667 spyOn(resources);
668 try {
669 // setUp: set secondary launcher and force it.
670 final String defaultSecondaryHome =
671 "com.android.test/com.android.test.TestDefaultSecondaryHome";
672 final ComponentName secondaryComp = ComponentName.unflattenFromString(
673 defaultSecondaryHome);
674 doReturn(defaultSecondaryHome).when(resources).getString(
675 com.android.internal.R.string.config_secondaryHomeComponent);
676 doReturn(true).when(resources).getBoolean(
677 com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
678 final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
679 assertEquals(secondaryComp, secondaryHomeIntent.getComponent());
680 final ActivityInfo aInfoSecondary = new ActivityInfo();
681 aInfoSecondary.name = secondaryComp.getClassName();
682 aInfoSecondary.applicationInfo = new ApplicationInfo();
683 aInfoSecondary.applicationInfo.packageName = secondaryComp.getPackageName();
684 doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
685 refEq(secondaryHomeIntent));
686 final Intent homeIntent = mService.getHomeIntent();
687 final ActivityInfo aInfoDefault = new ActivityInfo();
688 aInfoDefault.name = "fakeHomeActivity";
689 aInfoDefault.applicationInfo = new ApplicationInfo();
690 aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
691 doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
692 refEq(homeIntent));
693 // Let resolveActivities call to validate both main launcher and second launcher so that
694 // resolveActivities call does not work as enabler for secondary.
695 final List<ResolveInfo> resolutions1 = new ArrayList<>();
696 final ResolveInfo resolveInfo1 = new ResolveInfo();
697 resolveInfo1.activityInfo = new ActivityInfo();
698 resolveInfo1.activityInfo.name = aInfoDefault.name;
699 resolveInfo1.activityInfo.applicationInfo = aInfoDefault.applicationInfo;
700 resolutions1.add(resolveInfo1);
701 doReturn(resolutions1).when(mRootActivityContainer).resolveActivities(anyInt(),
702 refEq(homeIntent));
703 final List<ResolveInfo> resolutions2 = new ArrayList<>();
704 final ResolveInfo resolveInfo2 = new ResolveInfo();
705 resolveInfo2.activityInfo = new ActivityInfo();
706 resolveInfo2.activityInfo.name = aInfoSecondary.name;
707 resolveInfo2.activityInfo.applicationInfo = aInfoSecondary.applicationInfo;
708 resolutions2.add(resolveInfo2);
709 doReturn(resolutions2).when(mRootActivityContainer).resolveActivities(anyInt(),
710 refEq(secondaryHomeIntent));
711 doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
712 any(), anyInt(), anyBoolean());
713
714 // Run the test
715 final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
716 .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
717 assertEquals(secondaryComp.getClassName(), resolvedInfo.first.name);
718 assertEquals(secondaryComp.getPackageName(),
719 resolvedInfo.first.applicationInfo.packageName);
720 assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
721 } finally {
722 // tearDown
723 reset(resources);
724 }
725 }
726
727 /**
Chilun2ef71f72018-11-16 17:57:15 +0800728 * Tests that secondary home should be selected if default home not support secondary displays
729 * or there is no matched activity in the same package as selected default home.
730 */
731 @Test
732 public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() {
Riddle Hsuff9e8282019-04-24 23:55:11 +0800733 mockResolveHomeActivity();
Chilun2ef71f72018-11-16 17:57:15 +0800734
735 final List<ResolveInfo> resolutions = new ArrayList<>();
736 doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
737
738 final String secondaryHomeComponent = mService.mContext.getResources().getString(
739 com.android.internal.R.string.config_secondaryHomeComponent);
740 final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
741 final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
742 final ActivityInfo aInfoSecondary = new ActivityInfo();
743 aInfoSecondary.name = comp.getClassName();
744 doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
745 refEq(secondaryHomeIntent));
746
747 // Should fallback to secondary home if selected default home not support secondary displays
748 // or there is no matched activity in the same package as selected default home.
749 final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
750 .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
751
752 assertEquals(comp.getClassName(), resolvedInfo.first.name);
753 }
754
755 /**
756 * Tests that default home activity should be selected if it already support secondary displays.
757 */
758 @Test
759 public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() {
Riddle Hsuff9e8282019-04-24 23:55:11 +0800760 final ActivityInfo aInfoDefault = mockResolveHomeActivity();
Chilun2ef71f72018-11-16 17:57:15 +0800761
762 final List<ResolveInfo> resolutions = new ArrayList<>();
763 final ResolveInfo infoFake1 = new ResolveInfo();
764 infoFake1.activityInfo = new ActivityInfo();
765 infoFake1.activityInfo.name = "fakeActivity1";
766 infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
767 infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
768 final ResolveInfo infoFake2 = new ResolveInfo();
769 infoFake2.activityInfo = aInfoDefault;
770 resolutions.add(infoFake1);
771 resolutions.add(infoFake2);
772 doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
773
774 doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
775 any(), anyInt(), anyBoolean());
776
777 // Use default home activity if it support secondary displays.
778 final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
779 .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
780
781 assertEquals(aInfoDefault.applicationInfo.packageName,
782 resolvedInfo.first.applicationInfo.packageName);
783 assertEquals(aInfoDefault.name, resolvedInfo.first.name);
784 }
785
786 /**
787 * Tests that the first one that matches should be selected if there are multiple activities.
788 */
789 @Test
790 public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
Riddle Hsuff9e8282019-04-24 23:55:11 +0800791 mockResolveHomeActivity();
Chilun2ef71f72018-11-16 17:57:15 +0800792
793 final List<ResolveInfo> resolutions = new ArrayList<>();
794 final ResolveInfo infoFake1 = new ResolveInfo();
795 infoFake1.activityInfo = new ActivityInfo();
796 infoFake1.activityInfo.name = "fakeActivity1";
797 infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
798 infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
799 final ResolveInfo infoFake2 = new ResolveInfo();
800 infoFake2.activityInfo = new ActivityInfo();
801 infoFake2.activityInfo.name = "fakeActivity2";
802 infoFake2.activityInfo.applicationInfo = new ApplicationInfo();
803 infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2";
804 resolutions.add(infoFake1);
805 resolutions.add(infoFake2);
806 doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
807
808 doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
809 any(), anyInt(), anyBoolean());
810
811 // Use the first one of matched activities in the same package as selected default home.
812 final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
813 .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
814
815 assertEquals(infoFake1.activityInfo.applicationInfo.packageName,
816 resolvedInfo.first.applicationInfo.packageName);
817 assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name);
818 }
Riddle Hsuff9e8282019-04-24 23:55:11 +0800819
820 /**
Chilun59954982019-05-23 14:50:48 +0800821 * Mock {@link RootActivityContainer#resolveHomeActivity} for returning consistent activity
Riddle Hsuff9e8282019-04-24 23:55:11 +0800822 * info for test cases (the original implementation will resolve from the real package manager).
823 */
824 private ActivityInfo mockResolveHomeActivity() {
825 final Intent homeIntent = mService.getHomeIntent();
826 final ActivityInfo aInfoDefault = new ActivityInfo();
827 aInfoDefault.name = "fakeHomeActivity";
828 aInfoDefault.applicationInfo = new ApplicationInfo();
829 aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
830 doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
831 refEq(homeIntent));
832 return aInfoDefault;
833 }
Chilun59954982019-05-23 14:50:48 +0800834
835 /**
836 * Mock {@link RootActivityContainer#resolveSecondaryHomeActivity} for returning consistent
837 * activity info for test cases (the original implementation will resolve from the real package
838 * manager).
839 */
840 private void mockResolveSecondaryHomeActivity() {
841 final Intent secondaryHomeIntent = mService
842 .getSecondaryHomeIntent(null /* preferredPackage */);
843 final ActivityInfo aInfoSecondary = new ActivityInfo();
844 aInfoSecondary.name = "fakeSecondaryHomeActivity";
845 aInfoSecondary.applicationInfo = new ApplicationInfo();
846 aInfoSecondary.applicationInfo.packageName = "fakeSecondaryHomePackage";
847 doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootActivityContainer)
848 .resolveSecondaryHomeActivity(anyInt(), anyInt());
849 }
Wale Ogunwaled32da472018-11-16 07:19:28 -0800850}