blob: 9630b7d46e3c899952af3cdd1ed13fa154341d63 [file] [log] [blame]
Winson Chung0f7ec962018-05-03 18:03:15 -07001/*
Wale Ogunwale59507092018-10-29 09:00:30 -07002 * Copyright (C) 2018 The Android Open Source Project
Winson Chung0f7ec962018-05-03 18:03:15 -07003 *
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
Winson Chung0f7ec962018-05-03 18:03:15 -070015 */
16
Wale Ogunwale59507092018-10-29 09:00:30 -070017package com.android.server.wm;
Winson Chung0f7ec962018-05-03 18:03:15 -070018
Winson Chung2bc60c02019-05-16 17:36:05 -070019import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
Winson Chung0f7ec962018-05-03 18:03:15 -070020import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
21import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
22import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
Winson Chung2bc60c02019-05-16 17:36:05 -070023import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
Brett Chabota26eda92018-07-23 13:08:30 -070024
Riddle Hsu43233b72019-04-24 23:55:11 +080025import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
Riddle Hsu0e591722019-05-03 22:22:36 +080026import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
Louis Chang7cf84602019-05-30 15:35:40 +080027import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
Tadashi G. Takaokaa7a66952018-11-16 15:04:21 +090028import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
29import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
30import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
Wale Ogunwaleb73f3962018-11-20 07:58:22 -080031import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
Tadashi G. Takaokaa7a66952018-11-16 15:04:21 +090032import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
33import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
Louis Chang7cf84602019-05-30 15:35:40 +080034import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
Winson Chung0f7ec962018-05-03 18:03:15 -070035import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
Brett Chabota26eda92018-07-23 13:08:30 -070036
Louis Chang7cf84602019-05-30 15:35:40 +080037import static com.google.common.truth.Truth.assertThat;
38
Riddle Hsu43233b72019-04-24 23:55:11 +080039import static org.junit.Assert.assertFalse;
40import static org.junit.Assert.assertTrue;
lumark54284462019-03-05 20:44:27 +080041import static org.mockito.ArgumentMatchers.any;
Riddle Hsu0e591722019-05-03 22:22:36 +080042import static org.mockito.ArgumentMatchers.anyBoolean;
Riddle Hsu43233b72019-04-24 23:55:11 +080043import static org.mockito.ArgumentMatchers.anyInt;
lumark54284462019-03-05 20:44:27 +080044
Riddle Hsu609a8e22019-06-27 16:46:29 -060045import android.app.IApplicationThread;
Winson Chung0f7ec962018-05-03 18:03:15 -070046import android.content.ComponentName;
Winson Chung0f7ec962018-05-03 18:03:15 -070047import android.content.Intent;
Riddle Hsu609a8e22019-06-27 16:46:29 -060048import android.content.pm.ActivityInfo;
49import android.content.pm.ApplicationInfo;
Winson Chung0f7ec962018-05-03 18:03:15 -070050import android.platform.test.annotations.Presubmit;
Winson Chung0f7ec962018-05-03 18:03:15 -070051import android.view.IRecentsAnimationRunner;
Brett Chabota26eda92018-07-23 13:08:30 -070052
Brett Chabota26eda92018-07-23 13:08:30 -070053import androidx.test.filters.MediumTest;
Brett Chabota26eda92018-07-23 13:08:30 -070054
Riddle Hsu43233b72019-04-24 23:55:11 +080055import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
56
Winson Chung0f7ec962018-05-03 18:03:15 -070057import org.junit.Before;
58import org.junit.Test;
Winson Chung0f7ec962018-05-03 18:03:15 -070059
60/**
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +090061 * Build/Install/Run:
62 * atest WmTests:RecentsAnimationTest
Winson Chung0f7ec962018-05-03 18:03:15 -070063 */
64@MediumTest
65@Presubmit
Winson Chung0f7ec962018-05-03 18:03:15 -070066public class RecentsAnimationTest extends ActivityTestsBase {
Winson Chung0f7ec962018-05-03 18:03:15 -070067
Winson Chung2bc60c02019-05-16 17:36:05 -070068 private static final int TEST_USER_ID = 100;
69
Riddle Hsu43233b72019-04-24 23:55:11 +080070 private final ComponentName mRecentsComponent =
71 new ComponentName(mContext.getPackageName(), "RecentsActivity");
lumark54284462019-03-05 20:44:27 +080072 private RecentsAnimationController mRecentsAnimationController;
Winson Chung0f7ec962018-05-03 18:03:15 -070073
74 @Before
Winson Chung0f7ec962018-05-03 18:03:15 -070075 public void setUp() throws Exception {
lumark54284462019-03-05 20:44:27 +080076 mRecentsAnimationController = mock(RecentsAnimationController.class);
77 doReturn(mRecentsAnimationController).when(
78 mService.mWindowManager).getRecentsAnimationController();
Riddle Hsu43233b72019-04-24 23:55:11 +080079 doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
Wale Ogunwaleb73f3962018-11-20 07:58:22 -080080
81 final RecentTasks recentTasks = mService.getRecentTasks();
82 spyOn(recentTasks);
Wale Ogunwaleb73f3962018-11-20 07:58:22 -080083 doReturn(mRecentsComponent).when(recentTasks).getRecentsComponent();
Winson Chung0f7ec962018-05-03 18:03:15 -070084 }
85
86 @Test
Riddle Hsu0e591722019-05-03 22:22:36 +080087 public void testRecentsActivityVisiblility() {
88 ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
89 ActivityStack recentsStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
90 ACTIVITY_TYPE_RECENTS, true /* onTop */);
91 ActivityRecord recentActivity = new ActivityBuilder(mService)
92 .setComponent(mRecentsComponent)
93 .setCreateTask(true)
94 .setStack(recentsStack)
95 .build();
96 ActivityRecord topActivity = new ActivityBuilder(mService).setCreateTask(true).build();
97 topActivity.fullscreen = true;
98 topActivity.getActivityStack().moveToFront("testRecentsActivityVisiblility");
99
100 doCallRealMethod().when(mRootActivityContainer).ensureActivitiesVisible(
101 any() /* starting */, anyInt() /* configChanges */,
102 anyBoolean() /* preserveWindows */);
103
104 RecentsAnimationCallbacks recentsAnimation = startRecentsActivity(
105 mRecentsComponent, true /* getRecentsAnimation */);
106 // The launch-behind state should make the recents activity visible.
107 assertTrue(recentActivity.visible);
108
109 // Simulate the animation is cancelled without changing the stack order.
110 recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, true /* runSychronously */,
111 false /* sendUserLeaveHint */);
112 // The non-top recents activity should be invisible by the restored launch-behind state.
113 assertFalse(recentActivity.visible);
114 }
115
116 @Test
Riddle Hsu609a8e22019-06-27 16:46:29 -0600117 public void testPreloadRecentsActivity() {
118 // Ensure that the fake recent component can be resolved by the recents intent.
119 mockTaskRecordFactory(builder -> builder.setComponent(mRecentsComponent));
120 ActivityInfo aInfo = new ActivityInfo();
121 aInfo.applicationInfo = new ApplicationInfo();
122 aInfo.applicationInfo.uid = 10001;
123 aInfo.applicationInfo.targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
124 aInfo.packageName = aInfo.applicationInfo.packageName = mRecentsComponent.getPackageName();
125 aInfo.processName = "recents";
126 doReturn(aInfo).when(mSupervisor).resolveActivity(any() /* intent */, any() /* rInfo */,
127 anyInt() /* startFlags */, any() /* profilerInfo */);
128
129 // Assume its process is alive because the caller should be the recents service.
130 WindowProcessController wpc = new WindowProcessController(mService, aInfo.applicationInfo,
131 aInfo.processName, aInfo.applicationInfo.uid, 0 /* userId */,
132 mock(Object.class) /* owner */, mock(WindowProcessListener.class));
133 wpc.setThread(mock(IApplicationThread.class));
134 doReturn(wpc).when(mService).getProcessController(eq(wpc.mName), eq(wpc.mUid));
135
136 Intent recentsIntent = new Intent().setComponent(mRecentsComponent);
137 // Null animation indicates to preload.
138 mService.startRecentsActivity(recentsIntent, null /* assistDataReceiver */,
139 null /* recentsAnimationRunner */);
140
141 ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
142 ActivityStack recentsStack = display.getStack(WINDOWING_MODE_FULLSCREEN,
143 ACTIVITY_TYPE_RECENTS);
144 assertThat(recentsStack).isNotNull();
145
146 ActivityRecord recentsActivity = recentsStack.getTopActivity();
147 // The activity is started in background so it should be invisible and will be stopped.
148 assertThat(recentsActivity).isNotNull();
149 assertThat(mSupervisor.mStoppingActivities).contains(recentsActivity);
150 assertFalse(recentsActivity.visible);
151
152 // Assume it is stopped to test next use case.
153 recentsActivity.activityStoppedLocked(null /* newIcicle */, null /* newPersistentState */,
154 null /* description */);
155 mSupervisor.mStoppingActivities.remove(recentsActivity);
156
157 spyOn(recentsActivity);
158 // Start when the recents activity exists. It should ensure the configuration.
159 mService.startRecentsActivity(recentsIntent, null /* assistDataReceiver */,
160 null /* recentsAnimationRunner */);
161
162 verify(recentsActivity).ensureActivityConfiguration(anyInt() /* globalChanges */,
163 anyBoolean() /* preserveWindow */, eq(true) /* ignoreVisibility */);
164 assertThat(mSupervisor.mStoppingActivities).contains(recentsActivity);
165 }
166
167 @Test
Louis Chang7cf84602019-05-30 15:35:40 +0800168 public void testRestartRecentsActivity() throws Exception {
169 // Have a recents activity that is not attached to its process (ActivityRecord.app = null).
170 ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
171 ActivityStack recentsStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
172 ACTIVITY_TYPE_RECENTS, true /* onTop */);
173 ActivityRecord recentActivity = new ActivityBuilder(mService).setComponent(
174 mRecentsComponent).setCreateTask(true).setStack(recentsStack).build();
175 WindowProcessController app = recentActivity.app;
176 recentActivity.app = null;
177
178 // Start an activity on top.
179 new ActivityBuilder(mService).setCreateTask(true).build().getActivityStack().moveToFront(
180 "testRestartRecentsActivity");
181
182 doCallRealMethod().when(mRootActivityContainer).ensureActivitiesVisible(
183 any() /* starting */, anyInt() /* configChanges */,
184 anyBoolean() /* preserveWindows */);
185 doReturn(app).when(mService).getProcessController(eq(recentActivity.processName), anyInt());
186 ClientLifecycleManager lifecycleManager = mService.getLifecycleManager();
187 doNothing().when(lifecycleManager).scheduleTransaction(any());
188 AppWarnings appWarnings = mService.getAppWarningsLocked();
189 spyOn(appWarnings);
190 doNothing().when(appWarnings).onStartActivity(any());
191
192 startRecentsActivity();
193
194 // Recents activity must be restarted, but not be resumed while running recents animation.
195 verify(mRootActivityContainer.mStackSupervisor).startSpecificActivityLocked(
196 eq(recentActivity), eq(false), anyBoolean());
197 assertThat(recentActivity.getState()).isEqualTo(PAUSED);
198 }
199
200 @Test
Riddle Hsu43233b72019-04-24 23:55:11 +0800201 public void testSetLaunchTaskBehindOfTargetActivity() {
202 ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
203 display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class);
204 ActivityStack homeStack = display.getHomeStack();
205 // Assume the home activity support recents.
206 ActivityRecord targetActivity = homeStack.getTopActivity();
207 // Put another home activity in home stack.
208 ActivityRecord anotherHomeActivity = new ActivityBuilder(mService)
209 .setComponent(new ComponentName(mContext.getPackageName(), "Home2"))
210 .setCreateTask(true)
211 .setStack(homeStack)
212 .build();
213 // Start an activity on top so the recents activity can be started.
214 new ActivityBuilder(mService)
215 .setCreateTask(true)
216 .build()
217 .getActivityStack()
218 .moveToFront("Activity start");
219
220 // Start the recents animation.
221 RecentsAnimationCallbacks recentsAnimation = startRecentsActivity(
222 targetActivity.getTaskRecord().getBaseIntent().getComponent(),
223 true /* getRecentsAnimation */);
224 // Ensure launch-behind is set for being visible.
225 assertTrue(targetActivity.mLaunchTaskBehind);
226
227 anotherHomeActivity.moveFocusableActivityToTop("launchAnotherHome");
228 // The current top activity is not the recents so the animation should be canceled.
229 verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
230 eq(REORDER_KEEP_IN_PLACE), any() /* reason */);
231
232 // The test uses mocked RecentsAnimationController so we have to invoke the callback
233 // manually to simulate the flow.
234 recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, true /* runSychronously */,
235 false /* sendUserLeaveHint */);
236 // We should restore the launch-behind of the original target activity.
237 assertFalse(targetActivity.mLaunchTaskBehind);
238 }
239
240 @Test
Winson Chung65d66d32018-12-13 17:48:39 -0800241 public void testCancelAnimationOnVisibleStackOrderChange() {
242 ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
Tracy Zhou9c675d42019-04-08 00:32:40 -0700243 display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class);
Winson Chung65d66d32018-12-13 17:48:39 -0800244 ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
245 ACTIVITY_TYPE_STANDARD, true /* onTop */);
246 new ActivityBuilder(mService)
247 .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
248 .setCreateTask(true)
249 .setStack(fullscreenStack)
250 .build();
251 ActivityStack recentsStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
252 ACTIVITY_TYPE_RECENTS, true /* onTop */);
253 new ActivityBuilder(mService)
Winson Chung0f7ec962018-05-03 18:03:15 -0700254 .setComponent(mRecentsComponent)
255 .setCreateTask(true)
256 .setStack(recentsStack)
257 .build();
Winson Chung65d66d32018-12-13 17:48:39 -0800258 ActivityStack fullscreenStack2 = display.createStack(WINDOWING_MODE_FULLSCREEN,
259 ACTIVITY_TYPE_STANDARD, true /* onTop */);
260 new ActivityBuilder(mService)
261 .setComponent(new ComponentName(mContext.getPackageName(), "App2"))
Winson Chung0f7ec962018-05-03 18:03:15 -0700262 .setCreateTask(true)
263 .setStack(fullscreenStack2)
264 .build();
Winson Chung0f7ec962018-05-03 18:03:15 -0700265
266 // Start the recents animation
Riddle Hsu43233b72019-04-24 23:55:11 +0800267 startRecentsActivity();
Winson Chung0f7ec962018-05-03 18:03:15 -0700268
269 fullscreenStack.moveToFront("Activity start");
270
lumark54284462019-03-05 20:44:27 +0800271 // Ensure that the recents animation was canceled by cancelAnimationSynchronously().
Wale Ogunwaleb73f3962018-11-20 07:58:22 -0800272 verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
Winson Chung0f7ec962018-05-03 18:03:15 -0700273 eq(REORDER_KEEP_IN_PLACE), any());
lumark54284462019-03-05 20:44:27 +0800274
275 // Assume recents animation already started, set a state that cancel recents animation
276 // with screenshot.
277 doReturn(true).when(mRecentsAnimationController).shouldCancelWithDeferredScreenshot();
278 // Start another fullscreen activity.
279 fullscreenStack2.moveToFront("Activity start");
280
281 // Ensure that the recents animation was canceled by cancelOnNextTransitionStart().
282 verify(mRecentsAnimationController, times(1)).cancelOnNextTransitionStart();
Winson Chung0f7ec962018-05-03 18:03:15 -0700283 }
Winson Chung65d66d32018-12-13 17:48:39 -0800284
285 @Test
286 public void testKeepAnimationOnHiddenStackOrderChange() {
287 ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
288 ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
289 ACTIVITY_TYPE_STANDARD, true /* onTop */);
290 new ActivityBuilder(mService)
291 .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
292 .setCreateTask(true)
293 .setStack(fullscreenStack)
294 .build();
295 ActivityStack recentsStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
296 ACTIVITY_TYPE_RECENTS, true /* onTop */);
297 new ActivityBuilder(mService)
298 .setComponent(mRecentsComponent)
299 .setCreateTask(true)
300 .setStack(recentsStack)
301 .build();
302 ActivityStack fullscreenStack2 = display.createStack(WINDOWING_MODE_FULLSCREEN,
303 ACTIVITY_TYPE_STANDARD, true /* onTop */);
304 new ActivityBuilder(mService)
305 .setComponent(new ComponentName(mContext.getPackageName(), "App2"))
306 .setCreateTask(true)
307 .setStack(fullscreenStack2)
308 .build();
Winson Chung65d66d32018-12-13 17:48:39 -0800309
310 // Start the recents animation
Riddle Hsu43233b72019-04-24 23:55:11 +0800311 startRecentsActivity();
Winson Chung65d66d32018-12-13 17:48:39 -0800312
313 fullscreenStack.remove();
314
315 // Ensure that the recents animation was NOT canceled
316 verify(mService.mWindowManager, times(0)).cancelRecentsAnimationSynchronously(
317 eq(REORDER_KEEP_IN_PLACE), any());
lumark54284462019-03-05 20:44:27 +0800318 verify(mRecentsAnimationController, times(0)).cancelOnNextTransitionStart();
Winson Chung65d66d32018-12-13 17:48:39 -0800319 }
Riddle Hsu43233b72019-04-24 23:55:11 +0800320
Winson Chung2bc60c02019-05-16 17:36:05 -0700321 @Test
322 public void testMultipleUserHomeActivity_findUserHomeTask() {
323 ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
324 ActivityStack homeStack = display.getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
325 ActivityRecord otherUserHomeActivity = new ActivityBuilder(mService)
326 .setStack(homeStack)
327 .setCreateTask(true)
328 .setComponent(new ComponentName(mContext.getPackageName(), "Home2"))
329 .build();
330 otherUserHomeActivity.getTaskRecord().userId = TEST_USER_ID;
331
332 ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
333 ACTIVITY_TYPE_STANDARD, true /* onTop */);
334 new ActivityBuilder(mService)
335 .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
336 .setCreateTask(true)
337 .setStack(fullscreenStack)
338 .build();
339
340 doReturn(TEST_USER_ID).when(mService).getCurrentUserId();
341 doCallRealMethod().when(mRootActivityContainer).ensureActivitiesVisible(
342 any() /* starting */, anyInt() /* configChanges */,
343 anyBoolean() /* preserveWindows */);
344
345 startRecentsActivity(otherUserHomeActivity.getTaskRecord().getBaseIntent().getComponent(),
346 true);
347
348 // Ensure we find the task for the right user and it is made visible
349 assertTrue(otherUserHomeActivity.visible);
350 }
351
Riddle Hsu43233b72019-04-24 23:55:11 +0800352 private void startRecentsActivity() {
353 startRecentsActivity(mRecentsComponent, false /* getRecentsAnimation */);
354 }
355
356 /**
357 * @return non-null {@link RecentsAnimationCallbacks} if the given {@code getRecentsAnimation}
358 * is {@code true}.
359 */
360 private RecentsAnimationCallbacks startRecentsActivity(ComponentName recentsComponent,
361 boolean getRecentsAnimation) {
362 RecentsAnimationCallbacks[] recentsAnimation = { null };
363 if (getRecentsAnimation) {
364 doAnswer(invocation -> {
365 // The callback is actually RecentsAnimation.
366 recentsAnimation[0] = invocation.getArgument(2);
367 return null;
368 }).when(mService.mWindowManager).initializeRecentsAnimation(
369 anyInt() /* targetActivityType */, any() /* recentsAnimationRunner */,
370 any() /* callbacks */, anyInt() /* displayId */, any() /* recentTaskIds */);
371 }
372
373 Intent recentsIntent = new Intent();
374 recentsIntent.setComponent(recentsComponent);
375 mService.startRecentsActivity(recentsIntent, null /* assistDataReceiver */,
376 mock(IRecentsAnimationRunner.class));
377 return recentsAnimation[0];
378 }
Winson Chung0f7ec962018-05-03 18:03:15 -0700379}