blob: f6ff05b077d13a1864369e1f2df73ed7842c0589 [file] [log] [blame]
Bryce Lee4e4a3ec2017-09-27 08:25:03 -07001/*
Wale Ogunwale59507092018-10-29 09:00:30 -07002 * Copyright (C) 2018 The Android Open Source Project
Bryce Lee4e4a3ec2017-09-27 08:25:03 -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
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070015 */
16
Wale Ogunwale59507092018-10-29 09:00:30 -070017package com.android.server.wm;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070018
Michal Karpinski8596ded2018-11-14 14:43:48 +000019import static android.app.ActivityManager.PROCESS_STATE_TOP;
Bryce Lee93e7f792017-10-25 15:54:55 -070020import static android.app.ActivityManager.START_ABORTED;
21import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
Bryce Lee32e09ef2018-03-19 15:29:49 -070022import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
Bryce Lee93e7f792017-10-25 15:54:55 -070023import static android.app.ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
Bryce Lee32e09ef2018-03-19 15:29:49 -070024import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
Bryce Lee93e7f792017-10-25 15:54:55 -070025import static android.app.ActivityManager.START_NOT_VOICE_COMPATIBLE;
Bryce Lee32e09ef2018-03-19 15:29:49 -070026import static android.app.ActivityManager.START_PERMISSION_DENIED;
Bryce Lee2b8e0372018-04-05 17:01:37 -070027import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
Bryce Lee93e7f792017-10-25 15:54:55 -070028import static android.app.ActivityManager.START_SUCCESS;
29import static android.app.ActivityManager.START_SWITCHES_CANCELED;
Bryce Lee32e09ef2018-03-19 15:29:49 -070030import static android.app.ActivityManager.START_TASK_TO_FRONT;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070031import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
32import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
33import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
Bryce Lee32e09ef2018-03-19 15:29:49 -070034import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
35import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
Riddle Hsub70b36d2018-09-11 21:20:02 +080036import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
Brett Chabota26eda92018-07-23 13:08:30 -070037import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
Riddle Hsub70b36d2018-09-11 21:20:02 +080038import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
Brett Chabota26eda92018-07-23 13:08:30 -070039
Tadashi G. Takaokaa7a66952018-11-16 15:04:21 +090040import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
41import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
42import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
43import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
44import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
45import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
46import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
47import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
48import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
Wale Ogunwale59507092018-10-29 09:00:30 -070049import static com.android.server.wm.ActivityDisplay.POSITION_BOTTOM;
50import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
51import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
Brett Chabota26eda92018-07-23 13:08:30 -070052
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +090053import static com.google.common.truth.Truth.assertThat;
54
Brett Chabota26eda92018-07-23 13:08:30 -070055import static org.junit.Assert.assertEquals;
Riddle Hsub70b36d2018-09-11 21:20:02 +080056import static org.mockito.ArgumentMatchers.any;
57import static org.mockito.ArgumentMatchers.anyBoolean;
58import static org.mockito.ArgumentMatchers.anyInt;
59import static org.mockito.ArgumentMatchers.anyObject;
60import static org.mockito.ArgumentMatchers.eq;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070061
Bryce Lee93e7f792017-10-25 15:54:55 -070062import android.app.ActivityOptions;
63import android.app.IApplicationThread;
Riddle Hsub70b36d2018-09-11 21:20:02 +080064import android.content.ComponentName;
Bryce Lee93e7f792017-10-25 15:54:55 -070065import android.content.Intent;
66import android.content.pm.ActivityInfo;
Bryce Leead5b8322018-03-08 14:28:52 -080067import android.content.pm.ActivityInfo.WindowLayout;
Bryce Lee93e7f792017-10-25 15:54:55 -070068import android.content.pm.ApplicationInfo;
69import android.content.pm.IPackageManager;
Philip P. Moltmann6c644e62018-07-18 15:41:24 -070070import android.content.pm.PackageManagerInternal;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070071import android.graphics.Rect;
Bryce Lee93e7f792017-10-25 15:54:55 -070072import android.os.IBinder;
Michal Karpinski8596ded2018-11-14 14:43:48 +000073import android.os.Process;
Bryce Lee93e7f792017-10-25 15:54:55 -070074import android.os.RemoteException;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070075import android.platform.test.annotations.Presubmit;
Bryce Lee93e7f792017-10-25 15:54:55 -070076import android.service.voice.IVoiceInteractionSession;
Bryce Leead5b8322018-03-08 14:28:52 -080077import android.view.Gravity;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070078
Brett Chabota26eda92018-07-23 13:08:30 -070079import androidx.test.filters.SmallTest;
Brett Chabota26eda92018-07-23 13:08:30 -070080
Wale Ogunwale59507092018-10-29 09:00:30 -070081import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
82import com.android.server.wm.TaskRecord.TaskRecordFactory;
Bryce Lee93e7f792017-10-25 15:54:55 -070083
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +090084import org.junit.Before;
Brett Chabota26eda92018-07-23 13:08:30 -070085import org.junit.Test;
Bryce Lee2b8e0372018-04-05 17:01:37 -070086
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070087/**
Bryce Leed3624e12017-11-30 08:51:45 -080088 * Tests for the {@link ActivityStarter} class.
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070089 *
90 * Build/Install/Run:
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +090091 * atest WmTests:ActivityStarterTests
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070092 */
93@SmallTest
94@Presubmit
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070095public class ActivityStarterTests extends ActivityTestsBase {
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070096 private ActivityStarter mStarter;
Bryce Leed3624e12017-11-30 08:51:45 -080097 private ActivityStartController mController;
Michal Karpinski201bc0c2018-07-20 15:32:00 +010098 private ActivityMetricsLogger mActivityMetricsLogger;
Bryce Lee93e7f792017-10-25 15:54:55 -070099
100 private static final int PRECONDITION_NO_CALLER_APP = 1;
101 private static final int PRECONDITION_NO_INTENT_COMPONENT = 1 << 1;
102 private static final int PRECONDITION_NO_ACTIVITY_INFO = 1 << 2;
103 private static final int PRECONDITION_SOURCE_PRESENT = 1 << 3;
104 private static final int PRECONDITION_REQUEST_CODE = 1 << 4;
105 private static final int PRECONDITION_SOURCE_VOICE_SESSION = 1 << 5;
106 private static final int PRECONDITION_NO_VOICE_SESSION_SUPPORT = 1 << 6;
107 private static final int PRECONDITION_DIFFERENT_UID = 1 << 7;
108 private static final int PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION = 1 << 8;
109 private static final int PRECONDITION_CANNOT_START_ANY_ACTIVITY = 1 << 9;
110 private static final int PRECONDITION_DISALLOW_APP_SWITCHING = 1 << 10;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700111
Michal Karpinski201bc0c2018-07-20 15:32:00 +0100112 private static final int FAKE_CALLING_UID = 666;
113 private static final int FAKE_REAL_CALLING_UID = 667;
114 private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude";
Michal Karpinski8596ded2018-11-14 14:43:48 +0000115 private static final int UNIMPORTANT_UID = 12345;
Michal Karpinski201bc0c2018-07-20 15:32:00 +0100116
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900117 @Before
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700118 public void setUp() throws Exception {
Riddle Hsub70b36d2018-09-11 21:20:02 +0800119 setupActivityTaskManagerService();
Bryce Leed3624e12017-11-30 08:51:45 -0800120 mController = mock(ActivityStartController.class);
Michal Karpinski201bc0c2018-07-20 15:32:00 +0100121 mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
122 clearInvocations(mActivityMetricsLogger);
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700123 mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor,
Bryce Leed3624e12017-11-30 08:51:45 -0800124 mock(ActivityStartInterceptor.class));
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700125 }
126
127 @Test
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900128 public void testUpdateLaunchBounds() {
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700129 // When in a non-resizeable stack, the task bounds should be updated.
Bryce Lee18d51592017-10-25 10:22:19 -0700130 final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
131 .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
132 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
133 .build();
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700134 final Rect bounds = new Rect(10, 10, 100, 100);
135
136 mStarter.updateBounds(task, bounds);
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900137 assertEquals(bounds, task.getOverrideBounds());
Bryce Leef3c6a472017-11-14 14:53:06 -0800138 assertEquals(new Rect(), task.getStack().getOverrideBounds());
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700139
140 // When in a resizeable stack, the stack bounds should be updated as well.
Bryce Lee18d51592017-10-25 10:22:19 -0700141 final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
142 .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
143 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
144 .build();
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900145 assertThat((Object) task2.getStack()).isInstanceOf(PinnedActivityStack.class);
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700146 mStarter.updateBounds(task2, bounds);
147
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700148 verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700149 eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
150
151 // In the case of no animation, the stack and task bounds should be set immediately.
152 if (!ANIMATE) {
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900153 assertEquals(bounds, task2.getStack().getOverrideBounds());
154 assertEquals(bounds, task2.getOverrideBounds());
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700155 } else {
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900156 assertEquals(new Rect(), task2.getOverrideBounds());
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700157 }
158 }
Bryce Lee93e7f792017-10-25 15:54:55 -0700159
160 @Test
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900161 public void testStartActivityPreconditions() {
Bryce Lee93e7f792017-10-25 15:54:55 -0700162 verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED);
163 verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT,
164 START_INTENT_NOT_RESOLVED);
165 verifyStartActivityPreconditions(PRECONDITION_NO_ACTIVITY_INFO, START_CLASS_NOT_FOUND);
166 verifyStartActivityPreconditions(PRECONDITION_SOURCE_PRESENT | PRECONDITION_REQUEST_CODE,
167 Intent.FLAG_ACTIVITY_FORWARD_RESULT, START_FORWARD_AND_REQUEST_CONFLICT);
168 verifyStartActivityPreconditions(
169 PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
170 | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID,
171 START_NOT_VOICE_COMPATIBLE);
172 verifyStartActivityPreconditions(
173 PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
174 | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID
175 | PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION,
176 START_NOT_VOICE_COMPATIBLE);
177 verifyStartActivityPreconditions(PRECONDITION_CANNOT_START_ANY_ACTIVITY, START_ABORTED);
178 verifyStartActivityPreconditions(PRECONDITION_DISALLOW_APP_SWITCHING,
179 START_SWITCHES_CANCELED);
180 }
181
182 private static boolean containsConditions(int preconditions, int mask) {
183 return (preconditions & mask) == mask;
184 }
185
186 private void verifyStartActivityPreconditions(int preconditions, int expectedResult) {
187 verifyStartActivityPreconditions(preconditions, 0 /*launchFlags*/, expectedResult);
188 }
189
190 /**
191 * Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
192 * provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
193 * and the launch flags specified in the intent. The method constructs a call to
Bryce Lee4c9a5972017-12-01 22:14:24 -0800194 * {@link ActivityStarter#execute} based on these preconditions and ensures the result matches
195 * the expected. It is important to note that the method also checks side effects of the start,
196 * such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios.
Bryce Lee93e7f792017-10-25 15:54:55 -0700197 * @param preconditions A bitmask representing the preconditions for the launch
198 * @param launchFlags The launch flags to be provided by the launch {@link Intent}.
199 * @param expectedResult The expected result from the launch.
200 */
201 private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
202 int expectedResult) {
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700203 final ActivityTaskManagerService service = mService;
Bryce Lee93e7f792017-10-25 15:54:55 -0700204 final IPackageManager packageManager = mock(IPackageManager.class);
Bryce Leed3624e12017-11-30 08:51:45 -0800205 final ActivityStartController controller = mock(ActivityStartController.class);
Bryce Lee93e7f792017-10-25 15:54:55 -0700206
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700207 final ActivityStarter starter = new ActivityStarter(controller, service,
Bryce Leed3624e12017-11-30 08:51:45 -0800208 service.mStackSupervisor, mock(ActivityStartInterceptor.class));
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700209 prepareStarter(launchFlags);
Bryce Lee93e7f792017-10-25 15:54:55 -0700210 final IApplicationThread caller = mock(IApplicationThread.class);
211
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700212 final WindowProcessController wpc =
213 containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
214 ? null : new WindowProcessController(
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900215 service, mock(ApplicationInfo.class), null, 0, -1, null, null);
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700216 doReturn(wpc).when(service).getProcessController(anyObject());
Bryce Lee93e7f792017-10-25 15:54:55 -0700217
218 final Intent intent = new Intent();
219 intent.setFlags(launchFlags);
220
221 final ActivityInfo aInfo = containsConditions(preconditions, PRECONDITION_NO_ACTIVITY_INFO)
222 ? null : new ActivityInfo();
223
Bryce Lee93e7f792017-10-25 15:54:55 -0700224 IVoiceInteractionSession voiceSession =
225 containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
226 ? mock(IVoiceInteractionSession.class) : null;
227
228 // Create source token
229 final ActivityBuilder builder = new ActivityBuilder(service).setTask(
230 new TaskBuilder(service.mStackSupervisor).setVoiceSession(voiceSession).build());
231
Bryce Leefbd263b42018-03-07 10:33:55 -0800232 if (aInfo != null) {
233 aInfo.applicationInfo = new ApplicationInfo();
Bryce Leead5b8322018-03-08 14:28:52 -0800234 aInfo.applicationInfo.packageName =
235 ActivityBuilder.getDefaultComponent().getPackageName();
Bryce Leefbd263b42018-03-07 10:33:55 -0800236 }
237
Bryce Lee93e7f792017-10-25 15:54:55 -0700238 // Offset uid by one from {@link ActivityInfo} to simulate different uids.
239 if (containsConditions(preconditions, PRECONDITION_DIFFERENT_UID)) {
240 builder.setUid(aInfo.applicationInfo.uid + 1);
241 }
242
243 final ActivityRecord source = builder.build();
244
245 if (!containsConditions(preconditions, PRECONDITION_NO_INTENT_COMPONENT)) {
246 intent.setComponent(source.realActivity);
247 }
248
249 if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700250 doReturn(false).when(service).checkAppSwitchAllowedLocked(
Wale Ogunwalea6191b42018-05-09 07:41:32 -0700251 anyInt(), anyInt(), anyInt(), anyInt(), any());
Bryce Lee93e7f792017-10-25 15:54:55 -0700252 }
253
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900254 if (containsConditions(preconditions, PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
Bryce Lee93e7f792017-10-25 15:54:55 -0700255 doReturn(false).when(service.mStackSupervisor).checkStartAnyActivityPermission(
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100256 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
Winson Chungc9804e72018-05-15 11:01:44 -0700257 anyBoolean(), anyBoolean(), any(), any(), any());
Bryce Lee93e7f792017-10-25 15:54:55 -0700258 }
259
260 try {
261 if (containsConditions(preconditions,
262 PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION)) {
263 doAnswer((inv) -> {
264 throw new RemoteException();
265 }).when(packageManager).activitySupportsIntent(eq(source.realActivity), eq(intent),
266 any());
267 } else {
268 doReturn(!containsConditions(preconditions, PRECONDITION_NO_VOICE_SESSION_SUPPORT))
269 .when(packageManager).activitySupportsIntent(eq(source.realActivity),
270 eq(intent), any());
271 }
272 } catch (RemoteException e) {
273 }
274
275 final IBinder resultTo = containsConditions(preconditions, PRECONDITION_SOURCE_PRESENT)
276 || containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
277 ? source.appToken : null;
278
279 final int requestCode = containsConditions(preconditions, PRECONDITION_REQUEST_CODE)
280 ? 1 : 0;
281
Bryce Lee4c9a5972017-12-01 22:14:24 -0800282 final int result = starter.setCaller(caller)
283 .setIntent(intent)
284 .setActivityInfo(aInfo)
285 .setResultTo(resultTo)
286 .setRequestCode(requestCode)
287 .setReason("testLaunchActivityPermissionDenied")
288 .execute();
Bryce Lee93e7f792017-10-25 15:54:55 -0700289
290 // In some cases the expected result internally is different than the published result. We
291 // must use ActivityStarter#getExternalResult to translate.
292 assertEquals(ActivityStarter.getExternalResult(expectedResult), result);
293
294 // Ensure that {@link ActivityOptions} are aborted with unsuccessful result.
295 if (expectedResult != START_SUCCESS) {
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700296 final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
Bryce Lee4c9a5972017-12-01 22:14:24 -0800297 mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
Bryce Lee93e7f792017-10-25 15:54:55 -0700298 final ActivityOptions options = spy(ActivityOptions.makeBasic());
Bryce Lee4c9a5972017-12-01 22:14:24 -0800299
300 final int optionResult = optionStarter.setCaller(caller)
301 .setIntent(intent)
302 .setActivityInfo(aInfo)
303 .setResultTo(resultTo)
304 .setRequestCode(requestCode)
305 .setReason("testLaunchActivityPermissionDenied")
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100306 .setActivityOptions(new SafeActivityOptions(options))
Bryce Lee4c9a5972017-12-01 22:14:24 -0800307 .execute();
Bryce Lee93e7f792017-10-25 15:54:55 -0700308 verify(options, times(1)).abort();
309 }
310 }
Bryce Leeb802ea12017-11-15 21:25:03 -0800311
Riddle Hsub70b36d2018-09-11 21:20:02 +0800312 private ActivityStarter prepareStarter(@Intent.Flags int launchFlags) {
313 return prepareStarter(launchFlags, true /* mockGetLaunchStack */);
314 }
315
316 /**
317 * Creates a {@link ActivityStarter} with default parameters and necessary mocks.
318 *
319 * @param launchFlags The intent flags to launch activity.
320 * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for
321 * always launching to the testing stack. Set to false when allowing
322 * the activity can be launched to any stack that is decided by real
323 * implementation.
324 * @return A {@link ActivityStarter} with default setup.
325 */
326 private ActivityStarter prepareStarter(@Intent.Flags int launchFlags,
327 boolean mockGetLaunchStack) {
Bryce Leead5b8322018-03-08 14:28:52 -0800328 // always allow test to start activity.
329 doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
330 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
Winson Chungc9804e72018-05-15 11:01:44 -0700331 anyBoolean(), anyBoolean(), any(), any(), any());
Bryce Leead5b8322018-03-08 14:28:52 -0800332
333 // instrument the stack and task used.
Bryce Lee2b8e0372018-04-05 17:01:37 -0700334 final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
335 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
336 final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
337 .setCreateStack(false)
338 .build();
Bryce Leead5b8322018-03-08 14:28:52 -0800339
Bryce Leead5b8322018-03-08 14:28:52 -0800340 // use factory that only returns spy task.
341 final TaskRecordFactory factory = mock(TaskRecordFactory.class);
342 TaskRecord.setTaskRecordFactory(factory);
343
344 // return task when created.
345 doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any());
346
Riddle Hsub70b36d2018-09-11 21:20:02 +0800347 if (mockGetLaunchStack) {
348 // Direct starter to use spy stack.
349 doReturn(stack).when(mService.mStackSupervisor)
350 .getLaunchStack(any(), any(), any(), anyBoolean());
351 doReturn(stack).when(mService.mStackSupervisor)
Garfield Tan20d9e2f2018-11-16 15:42:29 -0800352 .getLaunchStack(any(), any(), any(), anyBoolean(), any());
Riddle Hsub70b36d2018-09-11 21:20:02 +0800353 }
Bryce Leead5b8322018-03-08 14:28:52 -0800354
Philip P. Moltmann6c644e62018-07-18 15:41:24 -0700355 // Set up mock package manager internal and make sure no unmocked methods are called
356 PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class,
357 invocation -> {
358 throw new RuntimeException("Not stubbed");
359 });
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700360 doReturn(mockPackageManager).when(mService).getPackageManagerInternalLocked();
Philip P. Moltmann6c644e62018-07-18 15:41:24 -0700361
362 // Never review permissions
363 doReturn(false).when(mockPackageManager).isPermissionsReviewRequired(any(), anyInt());
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700364 doNothing().when(mockPackageManager).grantEphemeralAccess(
365 anyInt(), any(), anyInt(), anyInt());
Philip P. Moltmann6c644e62018-07-18 15:41:24 -0700366
Bryce Lee32e09ef2018-03-19 15:29:49 -0700367 final Intent intent = new Intent();
368 intent.addFlags(launchFlags);
369 intent.setComponent(ActivityBuilder.getDefaultComponent());
370
371 final ActivityInfo info = new ActivityInfo();
372
373 info.applicationInfo = new ApplicationInfo();
374 info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
375
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700376 return new ActivityStarter(mController, mService,
Bryce Lee32e09ef2018-03-19 15:29:49 -0700377 mService.mStackSupervisor, mock(ActivityStartInterceptor.class))
378 .setIntent(intent)
379 .setActivityInfo(info);
Bryce Leead5b8322018-03-08 14:28:52 -0800380 }
381
382 /**
383 * Ensures that values specified at launch time are passed to {@link LaunchParamsModifier}
384 * when we are laying out a new task.
385 */
386 @Test
387 public void testCreateTaskLayout() {
388 // modifier for validating passed values.
389 final LaunchParamsModifier modifier = mock(LaunchParamsModifier.class);
390 mService.mStackSupervisor.getLaunchParamsController().registerModifier(modifier);
391
392 // add custom values to activity info to make unique.
393 final ActivityInfo info = new ActivityInfo();
394 final Rect launchBounds = new Rect(0, 0, 20, 30);
Bryce Leead5b8322018-03-08 14:28:52 -0800395
396 final WindowLayout windowLayout =
397 new WindowLayout(10, .5f, 20, 1.0f, Gravity.NO_GRAVITY, 1, 1);
398
399 info.windowLayout = windowLayout;
400 info.applicationInfo = new ApplicationInfo();
401 info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
402
403 // create starter.
Bryce Lee32e09ef2018-03-19 15:29:49 -0700404 final ActivityStarter optionStarter = prepareStarter(0 /* launchFlags */);
Bryce Leead5b8322018-03-08 14:28:52 -0800405
406 final ActivityOptions options = ActivityOptions.makeBasic();
407 options.setLaunchBounds(launchBounds);
408
409 // run starter.
410 optionStarter
Bryce Leead5b8322018-03-08 14:28:52 -0800411 .setReason("testCreateTaskLayout")
412 .setActivityInfo(info)
413 .setActivityOptions(new SafeActivityOptions(options))
414 .execute();
415
Garfield Tan706dbcb2018-10-15 11:33:02 -0700416 // verify that values are passed to the modifier. Values are passed twice -- once for
417 // setting initial state, another when task is created.
418 verify(modifier, times(2)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
Bryce Leead5b8322018-03-08 14:28:52 -0800419 any(), any());
420 }
Bryce Lee32e09ef2018-03-19 15:29:49 -0700421
422 /**
423 * This test ensures that if the intent is being delivered to a
424 */
425 @Test
426 public void testSplitScreenDeliverToTop() {
427 final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
428
429 final ActivityRecord focusActivity = new ActivityBuilder(mService)
430 .setCreateTask(true)
431 .build();
432
433 focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
434
435 final ActivityRecord reusableActivity = new ActivityBuilder(mService)
436 .setCreateTask(true)
437 .build();
438
439 // Create reusable activity after entering split-screen so that it is the top secondary
440 // stack.
441 reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
442
443 // Set focus back to primary.
Andrii Kulian6a6c4f12018-07-16 21:23:33 -0700444 final ActivityStack focusStack = focusActivity.getStack();
445 focusStack.moveToFront("testSplitScreenDeliverToTop");
Bryce Lee32e09ef2018-03-19 15:29:49 -0700446
447 doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
448
449 final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
450
451 // Ensure result is delivering intent to top.
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900452 assertEquals(START_DELIVERED_TO_TOP, result);
Bryce Lee32e09ef2018-03-19 15:29:49 -0700453 }
454
455 /**
456 * This test ensures that if the intent is being delivered to a split-screen unfocused task
457 * reports it is brought to front instead of delivering to top.
458 */
459 @Test
460 public void testSplitScreenTaskToFront() {
461 final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
462
463 // Create reusable activity here first. Setting the windowing mode of the primary stack
464 // will move the existing standard full screen stack to secondary, putting this one on the
465 // bottom.
466 final ActivityRecord reusableActivity = new ActivityBuilder(mService)
467 .setCreateTask(true)
468 .build();
469
470 reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
471
472 final ActivityRecord focusActivity = new ActivityBuilder(mService)
473 .setCreateTask(true)
474 .build();
475
476 // Enter split-screen. Primary stack should have focus.
477 focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
478
479 doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
480
481 final int result = starter.setReason("testSplitScreenMoveToFront").execute();
482
483 // Ensure result is moving task to front.
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900484 assertEquals(START_TASK_TO_FRONT, result);
Bryce Lee32e09ef2018-03-19 15:29:49 -0700485 }
Bryce Lee2b8e0372018-04-05 17:01:37 -0700486
487 /**
488 * Tests activity is cleaned up properly in a task mode violation.
489 */
490 @Test
491 public void testTaskModeViolation() {
492 final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
Andrii Kulian6a6c4f12018-07-16 21:23:33 -0700493 ((TestActivityDisplay) display).removeAllTasks();
Bryce Lee2b8e0372018-04-05 17:01:37 -0700494 assertNoTasks(display);
495
496 final ActivityStarter starter = prepareStarter(0);
497
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700498 final LockTaskController lockTaskController = mService.getLockTaskController();
Bryce Lee2b8e0372018-04-05 17:01:37 -0700499 doReturn(true).when(lockTaskController).isLockTaskModeViolation(any());
500
501 final int result = starter.setReason("testTaskModeViolation").execute();
502
503 assertEquals(START_RETURN_LOCK_TASK_MODE_VIOLATION, result);
504 assertNoTasks(display);
505 }
506
507 private void assertNoTasks(ActivityDisplay display) {
508 for (int i = display.getChildCount() - 1; i >= 0; --i) {
509 final ActivityStack stack = display.getChildAt(i);
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900510 assertThat(stack.getAllTasks()).isEmpty();
Bryce Lee2b8e0372018-04-05 17:01:37 -0700511 }
512 }
Michal Karpinski201bc0c2018-07-20 15:32:00 +0100513
514 /**
515 * This test ensures that activity starts are not being logged when the logging is disabled.
516 */
517 @Test
518 public void testActivityStartsLogging_noLoggingWhenDisabled() {
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700519 doReturn(false).when(mService).isActivityStartsLoggingEnabled();
Michal Karpinski201bc0c2018-07-20 15:32:00 +0100520 doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
521
522 ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK);
523 starter.setReason("testActivityStartsLogging_noLoggingWhenDisabled").execute();
524
525 // verify logging wasn't done
526 verify(mActivityMetricsLogger, never()).logActivityStart(any(), any(), any(), anyInt(),
527 any(), anyInt(), anyBoolean(), anyInt(), anyInt(), anyBoolean(), anyInt(), any(),
528 anyInt(), anyBoolean(), any(), anyBoolean());
529 }
530
531 /**
532 * This test ensures that activity starts are being logged when the logging is enabled.
533 */
534 @Test
535 public void testActivityStartsLogging_logsWhenEnabled() {
536 // note: conveniently this package doesn't have any activity visible
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700537 doReturn(true).when(mService).isActivityStartsLoggingEnabled();
Michal Karpinski201bc0c2018-07-20 15:32:00 +0100538 doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
539
540 ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
541 .setCallingUid(FAKE_CALLING_UID)
542 .setRealCallingUid(FAKE_REAL_CALLING_UID)
543 .setCallingPackage(FAKE_CALLING_PACKAGE)
544 .setOriginatingPendingIntent(null);
545
546 starter.setReason("testActivityStartsLogging_logsWhenEnabled").execute();
547
548 // verify the above activity start was logged
549 verify(mActivityMetricsLogger, times(1)).logActivityStart(any(), any(), any(),
550 eq(FAKE_CALLING_UID), eq(FAKE_CALLING_PACKAGE), anyInt(), anyBoolean(),
551 eq(FAKE_REAL_CALLING_UID), anyInt(), anyBoolean(), anyInt(),
552 eq(ActivityBuilder.getDefaultComponent().getPackageName()), anyInt(), anyBoolean(),
553 any(), eq(false));
554 }
Riddle Hsub70b36d2018-09-11 21:20:02 +0800555
556 /**
Michal Karpinski8596ded2018-11-14 14:43:48 +0000557 * This test ensures that unsupported usecases aren't aborted when background starts are
558 * allowed.
559 */
560 @Test
561 public void testBackgroundActivityStartsAllowed_noStartsAborted() {
562 doReturn(true).when(mService).isBackgroundActivityStartsEnabled();
563
564 runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted",
565 false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, false);
566 }
567
568 /**
569 * This test ensures that unsupported usecases are aborted when background starts are
570 * disallowed.
571 */
572 @Test
573 public void testBackgroundActivityStartsDisallowed_unsupportedStartsAborted() {
574 doReturn(false).when(mService).isBackgroundActivityStartsEnabled();
575
576 runAndVerifyBackgroundActivityStartsSubtest("disallowed_unsupportedUsecase_aborted",
577 true, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, false);
578 }
579
580 /**
581 * This test ensures that supported usecases aren't aborted when background starts are
582 * disallowed.
583 * The scenarios each have only one condidion that makes them supported.
584 */
585 @Test
586 public void testBackgroundActivityStartsDisallowed_supportedStartsNotAborted() {
587 doReturn(false).when(mService).isBackgroundActivityStartsEnabled();
588
589 runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted",
590 false, Process.ROOT_UID, false, PROCESS_STATE_TOP + 1, false);
591 runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted",
592 false, Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1, false);
593 runAndVerifyBackgroundActivityStartsSubtest("disallowed_hasVisibleWindow_notAborted",
594 false, UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1, false);
595 runAndVerifyBackgroundActivityStartsSubtest("disallowed_processStateTop_notAborted",
596 false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP, false);
597 runAndVerifyBackgroundActivityStartsSubtest("disallowed_hasForegroundActivities_notAborted",
598 false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, true);
599 }
600
601 private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
602 int testCallingUid, boolean hasVisibleWindow, int procState,
603 boolean hasForegroundActivities) {
604 // window visibility
605 doReturn(hasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid(
606 testCallingUid);
607 // process importance
608 doReturn(procState).when(mService).getUidStateLocked(testCallingUid);
609 // foreground activities
610 final IApplicationThread caller = mock(IApplicationThread.class);
611 final ApplicationInfo ai = new ApplicationInfo();
612 ai.uid = testCallingUid;
613 final WindowProcessController callerApp =
614 new WindowProcessController(mService, ai, null, testCallingUid, -1, null, null);
615 callerApp.setHasForegroundActivities(hasForegroundActivities);
616 doReturn(callerApp).when(mService).getProcessController(caller);
617
618 final ActivityOptions options = spy(ActivityOptions.makeBasic());
619 ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK).setCaller(caller)
620 .setCallingUid(testCallingUid).setActivityOptions(new SafeActivityOptions(options));
621
622 final int result = starter.setReason("testBackgroundActivityStarts_" + name).execute();
623
624 assertEquals(ActivityStarter.getExternalResult(
625 shouldHaveAborted ? START_ABORTED : START_SUCCESS), result);
626 verify(options, times(shouldHaveAborted ? 1 : 0)).abort();
627 }
628
629 /**
Riddle Hsub70b36d2018-09-11 21:20:02 +0800630 * This test ensures that when starting an existing single task activity on secondary display
631 * which is not the top focused display, it should deliver new intent to the activity and not
632 * create a new stack.
633 */
634 @Test
635 public void testDeliverIntentToTopActivityOfNonTopDisplay() {
636 final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
637 false /* mockGetLaunchStack */);
638
639 // Create a secondary display at bottom.
Riddle Hsufd4a0502018-10-16 01:05:16 +0800640 final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
641 mSupervisor.addChild(secondaryDisplay, POSITION_BOTTOM);
Riddle Hsub70b36d2018-09-11 21:20:02 +0800642 final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
643 ACTIVITY_TYPE_STANDARD, true /* onTop */);
644
645 // Create an activity record on the top of secondary display.
Riddle Hsufd4a0502018-10-16 01:05:16 +0800646 final ActivityRecord topActivityOnSecondaryDisplay = createSingleTaskActivityOn(stack);
Riddle Hsub70b36d2018-09-11 21:20:02 +0800647
648 // Put an activity on default display as the top focused activity.
649 new ActivityBuilder(mService).setCreateTask(true).build();
650
651 // Start activity with the same intent as {@code topActivityOnSecondaryDisplay}
652 // on secondary display.
653 final ActivityOptions options = ActivityOptions.makeBasic()
654 .setLaunchDisplayId(secondaryDisplay.mDisplayId);
655 final int result = starter.setReason("testDeliverIntentToTopActivityOfNonTopDisplay")
656 .setIntent(topActivityOnSecondaryDisplay.intent)
657 .setActivityOptions(options.toBundle())
658 .execute();
659
660 // Ensure result is delivering intent to top.
661 assertEquals(START_DELIVERED_TO_TOP, result);
662
663 // Ensure secondary display only creates one stack.
664 verify(secondaryDisplay, times(1)).createStack(anyInt(), anyInt(), anyBoolean());
665 }
666
667 /**
Riddle Hsufd4a0502018-10-16 01:05:16 +0800668 * This test ensures that when starting an existing non-top single task activity on secondary
669 * display which is the top focused display, it should bring the task to front without creating
670 * unused stack.
671 */
672 @Test
673 public void testBringTaskToFrontOnSecondaryDisplay() {
674 final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
675 false /* mockGetLaunchStack */);
676
677 // Create a secondary display with an activity.
678 final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
679 mSupervisor.addChild(secondaryDisplay, POSITION_TOP);
680 final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
681 secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
682 ACTIVITY_TYPE_STANDARD, false /* onTop */));
683
684 // Create another activity on top of the secondary display.
685 final ActivityStack topStack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
686 ACTIVITY_TYPE_STANDARD, true /* onTop */);
687 final TaskRecord topTask = new TaskBuilder(mSupervisor).setStack(topStack).build();
688 new ActivityBuilder(mService).setTask(topTask).build();
689
690 // Start activity with the same intent as {@code singleTaskActivity} on secondary display.
691 final ActivityOptions options = ActivityOptions.makeBasic()
692 .setLaunchDisplayId(secondaryDisplay.mDisplayId);
693 final int result = starter.setReason("testBringTaskToFrontOnSecondaryDisplay")
694 .setIntent(singleTaskActivity.intent)
695 .setActivityOptions(options.toBundle())
696 .execute();
697
698 // Ensure result is moving existing task to front.
699 assertEquals(START_TASK_TO_FRONT, result);
700
701 // Ensure secondary display only creates two stacks.
702 verify(secondaryDisplay, times(2)).createStack(anyInt(), anyInt(), anyBoolean());
703 }
704
705 private ActivityRecord createSingleTaskActivityOn(ActivityStack stack) {
706 final ComponentName componentName = ComponentName.createRelative(
707 DEFAULT_COMPONENT_PACKAGE_NAME,
708 DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity");
709 final TaskRecord taskRecord = new TaskBuilder(mSupervisor)
710 .setComponent(componentName)
711 .setStack(stack)
712 .build();
713 return new ActivityBuilder(mService)
714 .setComponent(componentName)
715 .setLaunchMode(LAUNCH_SINGLE_TASK)
716 .setTask(taskRecord)
717 .build();
718 }
719
720 /**
Riddle Hsub70b36d2018-09-11 21:20:02 +0800721 * This test ensures that a reused top activity in the top focused stack is able to be
722 * reparented to another display.
723 */
724 @Test
725 public void testReparentTopFocusedActivityToSecondaryDisplay() {
726 final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
727 false /* mockGetLaunchStack */);
728
729 // Create a secondary display at bottom.
730 final TestActivityDisplay secondaryDisplay = addNewActivityDisplayAt(POSITION_BOTTOM);
731 secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
732 true /* onTop */);
733
734 // Put an activity on default display as the top focused activity.
735 final ActivityRecord topActivity = new ActivityBuilder(mService)
736 .setCreateTask(true)
737 .setLaunchMode(LAUNCH_SINGLE_TASK)
738 .build();
739
740 // Start activity with the same intent as {@code topActivity} on secondary display.
741 final ActivityOptions options = ActivityOptions.makeBasic()
742 .setLaunchDisplayId(secondaryDisplay.mDisplayId);
743 starter.setReason("testReparentTopFocusedActivityToSecondaryDisplay")
744 .setIntent(topActivity.intent)
745 .setActivityOptions(options.toBundle())
746 .execute();
747
748 // Ensure the activity is moved to secondary display.
749 assertEquals(secondaryDisplay, topActivity.getDisplay());
750 }
Wale Ogunwale44f036f2017-09-29 05:09:09 -0700751}