blob: 85e8a1453e6f6a430c3c19376c31238a7dd0c591 [file] [log] [blame]
Riddle Hsu75016992018-09-20 20:37:14 +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
Wale Ogunwale59507092018-10-29 09:00:30 -070014 * limitations under the License
Riddle Hsu75016992018-09-20 20:37:14 +080015 */
16
Wale Ogunwale59507092018-10-29 09:00:30 -070017package com.android.server.wm;
Riddle Hsu75016992018-09-20 20:37:14 +080018
Riddle Hsu402b4402018-11-06 17:23:15 +080019import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
Riddle Hsu75016992018-09-20 20:37:14 +080020import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
Evan Roskyc5abbd82018-10-05 16:02:19 -070021import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
Riddle Hsu75016992018-09-20 20:37:14 +080022import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
23import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
24import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
Riddle Hsue10cea52018-10-16 23:33:23 +080025import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
Riddle Hsu75016992018-09-20 20:37:14 +080026
Tadashi G. Takaokaa7a66952018-11-16 15:04:21 +090027import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
28import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
29import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
30import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
31import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
Wale Ogunwale59507092018-10-29 09:00:30 -070032import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
Riddle Hsu75016992018-09-20 20:37:14 +080033
Riddle Hsubbb63c22018-10-03 12:28:29 +080034import static org.junit.Assert.assertEquals;
Evan Roskyc5abbd82018-10-05 16:02:19 -070035import static org.junit.Assert.assertFalse;
Riddle Hsue10cea52018-10-16 23:33:23 +080036import static org.junit.Assert.assertNull;
Riddle Hsu75016992018-09-20 20:37:14 +080037import static org.junit.Assert.assertTrue;
Riddle Hsu402b4402018-11-06 17:23:15 +080038import static org.mockito.ArgumentMatchers.any;
Louis Changf787e532019-01-15 12:46:49 +080039import static org.mockito.ArgumentMatchers.anyBoolean;
40import static org.mockito.ArgumentMatchers.eq;
41import static org.mockito.Mockito.doAnswer;
Riddle Hsu75016992018-09-20 20:37:14 +080042
Riddle Hsu7b766fd2019-01-28 21:14:59 +080043import android.app.TaskStackListener;
44import android.content.pm.ActivityInfo;
45import android.os.IBinder;
Riddle Hsu75016992018-09-20 20:37:14 +080046import android.platform.test.annotations.Presubmit;
Wale Ogunwale38f72bd2018-11-20 02:53:49 +000047
Riddle Hsu75016992018-09-20 20:37:14 +080048import androidx.test.filters.SmallTest;
Riddle Hsu75016992018-09-20 20:37:14 +080049
Riddle Hsu75016992018-09-20 20:37:14 +080050import org.junit.Test;
Riddle Hsu75016992018-09-20 20:37:14 +080051
Riddle Hsu7b766fd2019-01-28 21:14:59 +080052import java.util.ArrayList;
53import java.util.concurrent.CompletableFuture;
54import java.util.concurrent.TimeUnit;
55
Riddle Hsu75016992018-09-20 20:37:14 +080056/**
57 * Tests for the {@link ActivityDisplay} class.
58 *
59 * Build/Install/Run:
60 * atest WmTests:ActivityDisplayTests
61 */
62@SmallTest
63@Presubmit
Riddle Hsu75016992018-09-20 20:37:14 +080064public class ActivityDisplayTests extends ActivityTestsBase {
65
Riddle Hsubbb63c22018-10-03 12:28:29 +080066 @Test
67 public void testLastFocusedStackIsUpdatedWhenMovingStack() {
68 // Create a stack at bottom.
Wale Ogunwaled32da472018-11-16 07:19:28 -080069 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
70 final ActivityStack stack =
71 new StackBuilder(mRootActivityContainer).setOnTop(!ON_TOP).build();
Riddle Hsubbb63c22018-10-03 12:28:29 +080072 final ActivityStack prevFocusedStack = display.getFocusedStack();
73
74 stack.moveToFront("moveStackToFront");
75 // After moving the stack to front, the previous focused should be the last focused.
76 assertTrue(stack.isFocusedStackOnDisplay());
77 assertEquals(prevFocusedStack, display.getLastFocusedStack());
78
79 stack.moveToBack("moveStackToBack", null /* task */);
80 // After moving the stack to back, the stack should be the last focused.
81 assertEquals(stack, display.getLastFocusedStack());
82 }
83
Riddle Hsu75016992018-09-20 20:37:14 +080084 /**
85 * This test simulates the picture-in-picture menu activity launches an activity to fullscreen
86 * stack. The fullscreen stack should be the top focused for resuming correctly.
87 */
88 @Test
89 public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
90 // Create a pinned stack and move to front.
Wale Ogunwaled32da472018-11-16 07:19:28 -080091 final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack(
Riddle Hsu75016992018-09-20 20:37:14 +080092 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
93 final TaskRecord pinnedTask = new TaskBuilder(mService.mStackSupervisor)
94 .setStack(pinnedStack).build();
95 new ActivityBuilder(mService).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
96 .setTask(pinnedTask).build();
97 pinnedStack.moveToFront("movePinnedStackToFront");
98
99 // The focused stack should be the pinned stack.
100 assertTrue(pinnedStack.isFocusedStackOnDisplay());
101
102 // Create a fullscreen stack and move to front.
103 final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt(
Wale Ogunwaled32da472018-11-16 07:19:28 -0800104 mRootActivityContainer.getDefaultDisplay());
Riddle Hsu75016992018-09-20 20:37:14 +0800105 fullscreenStack.moveToFront("moveFullscreenStackToFront");
106
107 // The focused stack should be the fullscreen stack.
108 assertTrue(fullscreenStack.isFocusedStackOnDisplay());
109 }
110
111 /**
112 * Test {@link ActivityDisplay#mPreferredTopFocusableStack} will be cleared when the stack is
113 * removed or moved to back, and the focused stack will be according to z-order.
114 */
115 @Test
116 public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() {
117 // Create a display which only contains 2 stacks.
118 final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
119 final ActivityStack stack1 = createFullscreenStackWithSimpleActivityAt(display);
120 final ActivityStack stack2 = createFullscreenStackWithSimpleActivityAt(display);
121
122 // Put stack1 and stack2 on top.
123 stack1.moveToFront("moveStack1ToFront");
124 stack2.moveToFront("moveStack2ToFront");
125 assertTrue(stack2.isFocusedStackOnDisplay());
126
127 // Stack1 should be focused after moving stack2 to back.
128 stack2.moveToBack("moveStack2ToBack", null /* task */);
129 assertTrue(stack1.isFocusedStackOnDisplay());
130
131 // Stack2 should be focused after removing stack1.
132 display.removeChild(stack1);
133 assertTrue(stack2.isFocusedStackOnDisplay());
134 }
135
Riddle Hsu402b4402018-11-06 17:23:15 +0800136 /**
137 * Verifies {@link ActivityDisplay#remove} should not resume home stack on the removing display.
138 */
139 @Test
140 public void testNotResumeHomeStackOnRemovingDisplay() {
141 // Create a display which supports system decoration and allows reparenting stacks to
142 // another display when the display is removed.
143 final ActivityDisplay display = spy(createNewActivityDisplay());
144 doReturn(false).when(display).shouldDestroyContentOnRemove();
145 doReturn(true).when(display).supportsSystemDecorations();
Wale Ogunwaled32da472018-11-16 07:19:28 -0800146 mRootActivityContainer.addChild(display, ActivityDisplay.POSITION_TOP);
Riddle Hsu402b4402018-11-06 17:23:15 +0800147
148 // Put home stack on the display.
149 final ActivityStack homeStack = display.createStack(
150 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
151 final TaskRecord task = new TaskBuilder(mSupervisor).setStack(homeStack).build();
152 new ActivityBuilder(mService).setTask(task).build();
153 display.removeChild(homeStack);
154 final ActivityStack spiedHomeStack = spy(homeStack);
155 display.addChild(spiedHomeStack, ActivityDisplay.POSITION_TOP);
156 reset(spiedHomeStack);
157
158 // Put a finishing standard activity which will be reparented.
159 final ActivityStack stack = createFullscreenStackWithSimpleActivityAt(display);
160 stack.topRunningActivityLocked().makeFinishingLocked();
161
162 display.remove();
163
164 // The removed display should have no focused stack and its home stack should never resume.
165 assertNull(display.getFocusedStack());
166 verify(spiedHomeStack, never()).resumeTopActivityUncheckedLocked(any(), any());
167 }
168
Riddle Hsu75016992018-09-20 20:37:14 +0800169 private ActivityStack createFullscreenStackWithSimpleActivityAt(ActivityDisplay display) {
170 final ActivityStack fullscreenStack = display.createStack(
171 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP);
172 final TaskRecord fullscreenTask = new TaskBuilder(mService.mStackSupervisor)
173 .setStack(fullscreenStack).build();
174 new ActivityBuilder(mService).setTask(fullscreenTask).build();
175 return fullscreenStack;
176 }
Riddle Hsue10cea52018-10-16 23:33:23 +0800177
178 /**
179 * Verifies the correct activity is returned when querying the top running activity.
180 */
181 @Test
182 public void testTopRunningActivity() {
Wale Ogunwaled32da472018-11-16 07:19:28 -0800183 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
Riddle Hsue10cea52018-10-16 23:33:23 +0800184 final KeyguardController keyguard = mSupervisor.getKeyguardController();
Wale Ogunwaled32da472018-11-16 07:19:28 -0800185 final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
Louis Changf2835df2018-10-17 15:14:45 +0800186 final ActivityRecord activity = stack.getTopActivity();
187
188 // Create empty stack on top.
189 final ActivityStack emptyStack =
Wale Ogunwaled32da472018-11-16 07:19:28 -0800190 new StackBuilder(mRootActivityContainer).setCreateActivity(false).build();
Riddle Hsue10cea52018-10-16 23:33:23 +0800191
192 // Make sure the top running activity is not affected when keyguard is not locked.
193 assertTopRunningActivity(activity, display);
194
195 // Check to make sure activity not reported when it cannot show on lock and lock is on.
196 doReturn(true).when(keyguard).isKeyguardLocked();
197 assertEquals(activity, display.topRunningActivity());
198 assertNull(display.topRunningActivity(true /* considerKeyguardState */));
199
Louis Changf2835df2018-10-17 15:14:45 +0800200 // Move stack with activity to top.
201 stack.moveToFront("testStackToFront");
Riddle Hsue10cea52018-10-16 23:33:23 +0800202 assertEquals(stack, display.getFocusedStack());
203 assertEquals(activity, display.topRunningActivity());
204 assertNull(display.topRunningActivity(true /* considerKeyguardState */));
205
206 // Add activity that should be shown on the keyguard.
207 final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService)
208 .setCreateTask(true)
209 .setStack(stack)
210 .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
211 .build();
212
213 // Ensure the show when locked activity is returned.
214 assertTopRunningActivity(showWhenLockedActivity, display);
215
Louis Changf2835df2018-10-17 15:14:45 +0800216 // Move empty stack to front. The running activity in focusable stack which below the
217 // empty stack should be returned.
218 emptyStack.moveToFront("emptyStackToFront");
219 assertEquals(stack, display.getFocusedStack());
Riddle Hsue10cea52018-10-16 23:33:23 +0800220 assertTopRunningActivity(showWhenLockedActivity, display);
221 }
222
223 private static void assertTopRunningActivity(ActivityRecord top, ActivityDisplay display) {
224 assertEquals(top, display.topRunningActivity());
225 assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */));
226 }
Evan Roskyc5abbd82018-10-05 16:02:19 -0700227
228 /**
229 * This test enforces that alwaysOnTop stack is placed at proper position.
230 */
231 @Test
232 public void testAlwaysOnTopStackLocation() {
Wale Ogunwaled32da472018-11-16 07:19:28 -0800233 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
Evan Roskyc5abbd82018-10-05 16:02:19 -0700234 final ActivityStack alwaysOnTopStack = display.createStack(WINDOWING_MODE_FREEFORM,
235 ACTIVITY_TYPE_STANDARD, true /* onTop */);
236 final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
237 .setStack(alwaysOnTopStack).build();
238 alwaysOnTopStack.setAlwaysOnTop(true);
239 display.positionChildAtTop(alwaysOnTopStack, false /* includingParents */);
240 assertTrue(alwaysOnTopStack.isAlwaysOnTop());
241 // Ensure always on top state is synced to the children of the stack.
242 assertTrue(alwaysOnTopStack.getTopActivity().isAlwaysOnTop());
243 assertEquals(alwaysOnTopStack, display.getTopStack());
244
245 final ActivityStack pinnedStack = display.createStack(
246 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
247 assertEquals(pinnedStack, display.getPinnedStack());
248 assertEquals(pinnedStack, display.getTopStack());
249
250 final ActivityStack anotherAlwaysOnTopStack = display.createStack(
251 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
252 anotherAlwaysOnTopStack.setAlwaysOnTop(true);
253 display.positionChildAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
254 assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
255 int topPosition = display.getChildCount() - 1;
256 // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
257 // existing alwaysOnTop stack.
258 assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 1));
259
260 final ActivityStack nonAlwaysOnTopStack = display.createStack(
261 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
262 assertEquals(display, nonAlwaysOnTopStack.getDisplay());
263 topPosition = display.getChildCount() - 1;
264 // Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the
265 // existing other non-alwaysOnTop stacks.
266 assertEquals(nonAlwaysOnTopStack, display.getChildAt(topPosition - 3));
267
268 anotherAlwaysOnTopStack.setAlwaysOnTop(false);
269 display.positionChildAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
270 assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
271 // Ensure, when always on top is turned off for a stack, the stack is put just below all
272 // other always on top stacks.
273 assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 2));
274 anotherAlwaysOnTopStack.setAlwaysOnTop(true);
275
276 // Ensure always on top state changes properly when windowing mode changes.
277 anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
278 assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
279 assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 2));
280 anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
281 assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
282 assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 1));
283 }
Louis Changf787e532019-01-15 12:46:49 +0800284
285 @Test
286 public void testRemoveStackInWindowingModes() {
287 removeStackTests(() -> mRootActivityContainer.removeStacksInWindowingModes(
288 WINDOWING_MODE_FULLSCREEN));
289 }
290
291 @Test
292 public void testRemoveStackWithActivityTypes() {
293 removeStackTests(
294 () -> mRootActivityContainer.removeStacksWithActivityTypes(ACTIVITY_TYPE_STANDARD));
295 }
296
297 private void removeStackTests(Runnable runnable) {
298 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
299 final ActivityStack stack1 = display.createStack(WINDOWING_MODE_FULLSCREEN,
300 ACTIVITY_TYPE_STANDARD, ON_TOP);
301 final ActivityStack stack2 = display.createStack(WINDOWING_MODE_FULLSCREEN,
302 ACTIVITY_TYPE_STANDARD, ON_TOP);
303 final ActivityStack stack3 = display.createStack(WINDOWING_MODE_FULLSCREEN,
304 ACTIVITY_TYPE_STANDARD, ON_TOP);
305 final ActivityStack stack4 = display.createStack(WINDOWING_MODE_FULLSCREEN,
306 ACTIVITY_TYPE_STANDARD, ON_TOP);
307 final TaskRecord task1 = new TaskBuilder(mService.mStackSupervisor).setStack(
308 stack1).setTaskId(1).build();
309 final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor).setStack(
310 stack2).setTaskId(2).build();
311 final TaskRecord task3 = new TaskBuilder(mService.mStackSupervisor).setStack(
312 stack3).setTaskId(3).build();
313 final TaskRecord task4 = new TaskBuilder(mService.mStackSupervisor).setStack(
314 stack4).setTaskId(4).build();
315
316 // Reordering stacks while removing stacks.
317 doAnswer(invocation -> {
318 display.positionChildAtTop(stack3, false);
319 return true;
320 }).when(mSupervisor).removeTaskByIdLocked(eq(task4.taskId), anyBoolean(), anyBoolean(),
321 any());
322
323 // Removing stacks from the display while removing stacks.
324 doAnswer(invocation -> {
325 display.removeChild(stack2);
326 return true;
327 }).when(mSupervisor).removeTaskByIdLocked(eq(task2.taskId), anyBoolean(), anyBoolean(),
328 any());
329
330 runnable.run();
331 verify(mSupervisor).removeTaskByIdLocked(eq(task4.taskId), anyBoolean(), anyBoolean(),
332 any());
333 verify(mSupervisor).removeTaskByIdLocked(eq(task3.taskId), anyBoolean(), anyBoolean(),
334 any());
335 verify(mSupervisor).removeTaskByIdLocked(eq(task2.taskId), anyBoolean(), anyBoolean(),
336 any());
337 verify(mSupervisor).removeTaskByIdLocked(eq(task1.taskId), anyBoolean(), anyBoolean(),
338 any());
339 }
Riddle Hsu7b766fd2019-01-28 21:14:59 +0800340
341 /**
342 * Ensures that {@link TaskStackListener} can receive callback about the activity in size
343 * compatibility mode.
344 */
345 @Test
346 public void testHandleActivitySizeCompatMode() throws Exception {
347 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
348 final ActivityRecord activity = createFullscreenStackWithSimpleActivityAt(
349 display).topRunningActivityLocked();
350 activity.setState(ActivityStack.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
351 activity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
352 activity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
353 activity.getTaskRecord().getConfiguration().windowConfiguration.getBounds().set(
354 0, 0, 1000, 2000);
355
356 final ArrayList<CompletableFuture<IBinder>> resultWrapper = new ArrayList<>();
357 mService.getTaskChangeNotificationController().registerTaskStackListener(
358 new TaskStackListener() {
359 @Override
360 public void onSizeCompatModeActivityChanged(int displayId,
361 IBinder activityToken) {
362 resultWrapper.get(0).complete(activityToken);
363 }
364 });
365
366 // Expect the exact component name when the activity is in size compatible mode.
367 activity.getResolvedOverrideConfiguration().windowConfiguration.getBounds().set(
368 0, 0, 800, 1600);
369 resultWrapper.add(new CompletableFuture<>());
370 display.handleActivitySizeCompatModeIfNeeded(activity);
371
372 assertEquals(activity.appToken, resultWrapper.get(0).get(2, TimeUnit.SECONDS));
373
374 // Expect null component name when switching to non-size-compat mode activity.
375 activity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
376 resultWrapper.set(0, new CompletableFuture<>());
377 display.handleActivitySizeCompatModeIfNeeded(activity);
378
379 assertNull(resultWrapper.get(0).get(2, TimeUnit.SECONDS));
380 }
Riddle Hsu75016992018-09-20 20:37:14 +0800381}