blob: 631de99dc3ad51b0778695156099fb347448e6d1 [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
19import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
20import static android.app.ActivityManager.START_TASK_TO_FRONT;
21import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
22import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
23import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
24import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
25import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
26import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
27import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
28import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
29import static android.view.Display.DEFAULT_DISPLAY;
30import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
31import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
32import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
33import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
34import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
35import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
36import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
37import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
38import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
39import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
40import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
41import static com.google.common.truth.Truth.assertThat;
42import 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;
52
53import android.app.ActivityOptions;
54import android.app.WaitResult;
55import android.content.pm.ActivityInfo;
56import android.content.pm.ApplicationInfo;
57import android.graphics.Rect;
58import android.os.Build;
59import android.platform.test.annotations.Presubmit;
60import androidx.test.filters.MediumTest;
61import org.junit.Before;
62import org.junit.Test;
63
64import java.util.ArrayList;
65
66/**
67 * Tests for the {@link ActivityStackSupervisor} class.
68 *
69 * Build/Install/Run:
70 * atest WmTests:ActivityStackSupervisorTests
71 */
72@MediumTest
73@Presubmit
74public class RootActivityContainerTests extends ActivityTestsBase {
75 private ActivityStack mFullscreenStack;
76
77 @Before
78 public void setUp() throws Exception {
79 setupActivityTaskManagerService();
80 mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack(
81 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
82 }
83
84 /**
85 * This test ensures that we do not try to restore a task based off an invalid task id. We
86 * should expect {@code null} to be returned in this case.
87 */
88 @Test
89 public void testRestoringInvalidTask() {
90 ((TestActivityDisplay) mRootActivityContainer.getDefaultDisplay()).removeAllTasks();
91 TaskRecord task = mRootActivityContainer.anyTaskForId(0 /*taskId*/,
92 MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
93 assertNull(task);
94 }
95
96 /**
97 * This test ensures that an existing task in the pinned stack is moved to the fullscreen
98 * activity stack when a new task is added.
99 */
100 @Test
101 public void testReplacingTaskInPinnedStack() {
102 final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
103 .setStack(mFullscreenStack).build();
104 final TaskRecord firstTask = firstActivity.getTask();
105
106 final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
107 .setStack(mFullscreenStack).build();
108 final TaskRecord secondTask = secondActivity.getTask();
109
110 mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
111
112 // Ensure full screen stack has both tasks.
113 ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
114
115 // Move first activity to pinned stack.
116 final Rect sourceBounds = new Rect();
117 mRootActivityContainer.moveActivityToPinnedStack(firstActivity, sourceBounds,
118 0f /*aspectRatio*/, "initialMove");
119
120 final ActivityDisplay display = mFullscreenStack.getDisplay();
121 ActivityStack pinnedStack = display.getPinnedStack();
122 // Ensure a task has moved over.
123 ensureStackPlacement(pinnedStack, firstTask);
124 ensureStackPlacement(mFullscreenStack, secondTask);
125
126 // Move second activity to pinned stack.
127 mRootActivityContainer.moveActivityToPinnedStack(secondActivity, sourceBounds,
128 0f /*aspectRatio*/, "secondMove");
129
130 // Need to get stacks again as a new instance might have been created.
131 pinnedStack = display.getPinnedStack();
132 mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
133 // Ensure stacks have swapped tasks.
134 ensureStackPlacement(pinnedStack, secondTask);
135 ensureStackPlacement(mFullscreenStack, firstTask);
136 }
137
138 private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
139 final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
140 assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
141
142 if (tasks == null) {
143 return;
144 }
145
146 for (TaskRecord task : tasks) {
147 assertTrue(stackTasks.contains(task));
148 }
149 }
150
151 @Test
152 public void testApplySleepTokens() {
153 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
154 final KeyguardController keyguard = mSupervisor.getKeyguardController();
155 final ActivityStack stack = mock(ActivityStack.class);
156 display.addChild(stack, 0 /* position */);
157
158 // Make sure we wake and resume in the case the display is turning on and the keyguard is
159 // not showing.
160 verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
161 false /* displayShouldSleep */, true /* isFocusedStack */,
162 false /* keyguardShowing */, true /* expectWakeFromSleep */,
163 true /* expectResumeTopActivity */);
164
165 // Make sure we wake and don't resume when the display is turning on and the keyguard is
166 // showing.
167 verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
168 false /* displayShouldSleep */, true /* isFocusedStack */,
169 true /* keyguardShowing */, true /* expectWakeFromSleep */,
170 false /* expectResumeTopActivity */);
171
172 // Make sure we wake and don't resume when the display is turning on and the keyguard is
173 // not showing as unfocused.
174 verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
175 false /* displayShouldSleep */, false /* isFocusedStack */,
176 false /* keyguardShowing */, true /* expectWakeFromSleep */,
177 false /* expectResumeTopActivity */);
178
179 // Should not do anything if the display state hasn't changed.
180 verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
181 false /* displayShouldSleep */, true /* isFocusedStack */,
182 false /* keyguardShowing */, false /* expectWakeFromSleep */,
183 false /* expectResumeTopActivity */);
184 }
185
186 private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
187 ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
188 boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
189 boolean expectResumeTopActivity) {
190 reset(stack);
191
192 doReturn(displayShouldSleep).when(display).shouldSleep();
193 doReturn(displaySleeping).when(display).isSleeping();
194 doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
195
196 doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
197 doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
198 mRootActivityContainer.applySleepTokens(true);
199 verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
200 verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
201 null /* target */, null /* targetOptions */);
202 }
203
204 /**
205 * Verifies that removal of activity with task and stack is done correctly.
206 */
207 @Test
208 public void testRemovingStackOnAppCrash() {
209 final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay();
210 final int originalStackCount = defaultDisplay.getChildCount();
211 final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
212 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
213 final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
214 .setStack(stack).build();
215
216 assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
217
218 // Let's pretend that the app has crashed.
219 firstActivity.app.setThread(null);
220 mRootActivityContainer.finishTopCrashedActivities(firstActivity.app, "test");
221
222 // Verify that the stack was removed.
223 assertEquals(originalStackCount, defaultDisplay.getChildCount());
224 }
225
226 @Test
227 public void testFocusability() {
228 final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
229 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
230 final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
231 .setStack(stack).build();
232
233 // Under split screen primary we should be focusable when not minimized
234 mRootActivityContainer.setDockedStackMinimized(false);
235 assertTrue(stack.isFocusable());
236 assertTrue(activity.isFocusable());
237
238 // Under split screen primary we should not be focusable when minimized
239 mRootActivityContainer.setDockedStackMinimized(true);
240 assertFalse(stack.isFocusable());
241 assertFalse(activity.isFocusable());
242
243 final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack(
244 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
245 final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
246 .setStack(pinnedStack).build();
247
248 // We should not be focusable when in pinned mode
249 assertFalse(pinnedStack.isFocusable());
250 assertFalse(pinnedActivity.isFocusable());
251
252 // Add flag forcing focusability.
253 pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
254
255 // We should not be focusable when in pinned mode
256 assertTrue(pinnedStack.isFocusable());
257 assertTrue(pinnedActivity.isFocusable());
258
259 // Without the overridding activity, stack should not be focusable.
260 pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability",
261 REMOVE_TASK_MODE_DESTROYING);
262 assertFalse(pinnedStack.isFocusable());
263 }
264
265 /**
266 * Verify that split-screen primary stack will be chosen if activity is launched that targets
267 * split-screen secondary, but a matching existing instance is found on top of split-screen
268 * primary stack.
269 */
270 @Test
271 public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
272 // Create primary split-screen stack with a task and an activity.
273 final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
274 .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
275 true /* onTop */);
276 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
277 final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
278
279 // Find a launch stack for the top activity in split-screen primary, while requesting
280 // split-screen secondary.
281 final ActivityOptions options = ActivityOptions.makeBasic();
282 options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
283 final ActivityStack result =
284 mRootActivityContainer.getLaunchStack(r, options, task, true /* onTop */);
285
286 // Assert that the primary stack is returned.
287 assertEquals(primaryStack, result);
288 }
289
290 /**
291 * Verify split-screen primary stack & task can resized by
292 * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
293 */
294 @Test
295 public void testResizeDockedStackForSplitScreenPrimary() {
296 final Rect taskSize = new Rect(0, 0, 600, 600);
297 final Rect stackSize = new Rect(0, 0, 300, 300);
298
299 // Create primary split-screen stack with a task.
300 final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
301 .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
302 true /* onTop */);
303 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
304
305 // Resize dock stack.
306 mService.resizeDockedStack(stackSize, taskSize, null, null, null);
307
308 // Verify dock stack & its task bounds if is equal as resized result.
309 assertEquals(primaryStack.getBounds(), stackSize);
310 assertEquals(task.getBounds(), taskSize);
311 }
312
313 /**
314 * Verify that home stack would be moved to front when the top activity is Recents.
315 */
316 @Test
317 public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
318 // Create stack/task on default display.
319 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
320 final TestActivityStack targetStack =
321 new StackBuilder(mRootActivityContainer).setOnTop(false).build();
322 final TaskRecord targetTask = targetStack.getChildAt(0);
323
324 // Create Recents on top of the display.
325 final ActivityStack stack = new StackBuilder(mRootActivityContainer).setActivityType(
326 ACTIVITY_TYPE_RECENTS).build();
327
328 final String reason = "findTaskToMoveToFront";
329 mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
330 false);
331
332 verify(display).moveHomeStackToFront(contains(reason));
333 }
334
335 /**
336 * Verify that home stack won't be moved to front if the top activity on other display is
337 * Recents.
338 */
339 @Test
340 public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
341 // Create stack/task on default display.
342 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
343 final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
344 ACTIVITY_TYPE_STANDARD, false /* onTop */);
345 final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
346
347 // Create Recents on secondary display.
348 final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
349 ActivityDisplay.POSITION_TOP);
350 final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
351 ACTIVITY_TYPE_RECENTS, true /* onTop */);
352 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
353 new ActivityBuilder(mService).setTask(task).build();
354
355 final String reason = "findTaskToMoveToFront";
356 mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
357 false);
358
359 verify(display, never()).moveHomeStackToFront(contains(reason));
360 }
361
362 /**
363 * Verify if a stack is not at the topmost position, it should be able to resume its activity if
364 * the stack is the top focused.
365 */
366 @Test
367 public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
368 // Create a stack at bottom.
369 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
370 final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
371 ACTIVITY_TYPE_STANDARD, false /* onTop */));
372 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
373 final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
374 display.positionChildAtBottom(targetStack);
375
376 // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
377 // is the current top focused stack.
378 assertFalse(targetStack.isTopStackOnDisplay());
379 doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack();
380
381 // Use the stack as target to resume.
382 mRootActivityContainer.resumeFocusedStacksTopActivities(
383 targetStack, activity, null /* targetOptions */);
384
385 // Verify the target stack should resume its activity.
386 verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
387 eq(activity), eq(null /* targetOptions */));
388 }
389
390 /**
391 * Tests home activities that targeted sdk before Q cannot start on secondary display.
392 */
393 @Test
394 public void testStartHomeTargetSdkBeforeQ() throws Exception {
395 final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
396 mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
397 doReturn(true).when(secondDisplay).supportsSystemDecorations();
398
399 final ActivityInfo info = new ActivityInfo();
400 info.launchMode = LAUNCH_MULTIPLE;
401 info.applicationInfo = new ApplicationInfo();
402 info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
403 assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
404 false /* allowInstrumenting */));
405
406 info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
407 assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
408 false /* allowInstrumenting */));
409 }
410
411 /**
412 * Tests that home activities can be started on the displays that supports system decorations.
413 */
414 @Test
415 public void testStartHomeOnAllDisplays() {
416 // Create secondary displays.
417 final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
418 mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
419 doReturn(true).when(secondDisplay).supportsSystemDecorations();
420
421 // Create mock tasks and other necessary mocks.
422 TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false);
423 final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class);
424 TaskRecord.setTaskRecordFactory(factory);
425 doAnswer(i -> taskBuilder.build()).when(factory)
426 .create(any(), anyInt(), any(), any(), any(), any());
427 doReturn(true).when(mRootActivityContainer)
428 .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
429 doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
430 any(), anyInt(), anyBoolean());
431
432 mRootActivityContainer.startHomeOnAllDisplays(0, "testStartHome");
433
434 assertTrue(mRootActivityContainer.getDefaultDisplay().getTopStack().isActivityTypeHome());
435 assertNotNull(secondDisplay.getTopStack());
436 assertTrue(secondDisplay.getTopStack().isActivityTypeHome());
437 }
438
439 /**
440 * Tests that home activities won't be started before booting when display added.
441 */
442 @Test
443 public void testNotStartHomeBeforeBoot() {
444 final int displayId = 1;
445 final boolean isBooting = mService.mAmInternal.isBooting();
446 final boolean isBooted = mService.mAmInternal.isBooted();
447 try {
448 mService.mAmInternal.setBooting(false);
449 mService.mAmInternal.setBooted(false);
450 mRootActivityContainer.onDisplayAdded(displayId);
451 verify(mRootActivityContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
452 } finally {
453 mService.mAmInternal.setBooting(isBooting);
454 mService.mAmInternal.setBooted(isBooted);
455 }
456 }
457
458 /**
459 * Tests whether home can be started if being instrumented.
460 */
461 @Test
462 public void testCanStartHomeWhenInstrumented() {
463 final ActivityInfo info = new ActivityInfo();
464 info.applicationInfo = new ApplicationInfo();
465 final WindowProcessController app = mock(WindowProcessController.class);
466 doReturn(app).when(mService).getProcessController(any(), anyInt());
467
468 // Can not start home if we don't want to start home while home is being instrumented.
469 doReturn(true).when(app).isInstrumenting();
470 assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
471 false /* allowInstrumenting*/));
472
473 // Can start home for other cases.
474 assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
475 true /* allowInstrumenting*/));
476
477 doReturn(false).when(app).isInstrumenting();
478 assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
479 false /* allowInstrumenting*/));
480 assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
481 true /* allowInstrumenting*/));
482 }
483}