blob: 8d2c3dd805382fd772aa3109d193b8c55b648189 [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() {
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
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() {
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
328 public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
329 // Create stack/task on default display.
330 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
Yunfan Chen279f5582018-12-12 15:24:50 -0800331 final TestActivityStack targetStack = (TestActivityStack) new StackBuilder(
332 mRootActivityContainer).setOnTop(false).build();
Wale Ogunwaled32da472018-11-16 07:19:28 -0800333 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
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
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 /**
Andrii Kulian3ff5a0b2019-05-16 13:18:04 -0700402 * Verify that home activity will be started on a display even if another display has a
403 * focusable activity.
404 */
405 @Test
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
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 /**
Jorim Jaggia5cf6802019-04-26 19:43:11 +0200454 * Verify that a lingering transition is being executed in case the activity to be resumed is
455 * already resumed
456 */
457 @Test
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
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 /**
Wale Ogunwaled32da472018-11-16 07:19:28 -0800500 * Tests that home activities can be started on the displays that supports system decorations.
501 */
Chilun2afb94e2018-12-25 20:42:45 +0800502 @Test
503 public void testStartHomeOnAllDisplays() {
Riddle Hsuff9e8282019-04-24 23:55:11 +0800504 mockResolveHomeActivity();
505
Wale Ogunwaled32da472018-11-16 07:19:28 -0800506 // 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.
Riddle Hsuff9e8282019-04-24 23:55:11 +0800512 mockTaskRecordFactory();
Wale Ogunwaled32da472018-11-16 07:19:28 -0800513 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
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
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 }
Chilun2ef71f72018-11-16 17:57:15 +0800569
570 /**
Chilun85ebc0d2019-04-15 16:00:53 +0800571 * Tests that secondary home activity should not be resolved if device is still locked.
572 */
573 @Test
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
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 /**
Riddle Hsuff9e8282019-04-24 23:55:11 +0800613 * 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
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 /**
Chilun2ef71f72018-11-16 17:57:15 +0800633 * Tests that secondary home should be selected if default home not set.
634 */
635 @Test
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 /**
Keun young Park340546a2019-05-06 16:27:44 -0700660 * Tests that the default secondary home activity is always picked when it is in forced by
661 * config_useSystemProvidedLauncherForSecondary.
662 */
663 @Test
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 /**
Chilun2ef71f72018-11-16 17:57:15 +0800727 * 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
731 public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() {
Riddle Hsuff9e8282019-04-24 23:55:11 +0800732 mockResolveHomeActivity();
Chilun2ef71f72018-11-16 17:57:15 +0800733
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
758 public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() {
Riddle Hsuff9e8282019-04-24 23:55:11 +0800759 final ActivityInfo aInfoDefault = mockResolveHomeActivity();
Chilun2ef71f72018-11-16 17:57:15 +0800760
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
789 public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
Riddle Hsuff9e8282019-04-24 23:55:11 +0800790 mockResolveHomeActivity();
Chilun2ef71f72018-11-16 17:57:15 +0800791
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 }
Riddle Hsuff9e8282019-04-24 23:55:11 +0800818
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 */
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 }
Wale Ogunwaled32da472018-11-16 07:19:28 -0800833}