blob: 5906db306f110b27db97aa628a159834a7a3f175 [file] [log] [blame]
Bryce Lee4e4a3ec2017-09-27 08:25:03 -07001/*
2 * Copyright (C) 2017 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.am;
18
Bryce Lee93e7f792017-10-25 15:54:55 -070019import static android.app.ActivityManager.START_ABORTED;
20import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
21import static android.app.ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
22import static android.app.ActivityManager.START_NOT_VOICE_COMPATIBLE;
23import static android.app.ActivityManager.START_SUCCESS;
24import static android.app.ActivityManager.START_SWITCHES_CANCELED;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070025import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
26import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
27import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
28
Bryce Lee93e7f792017-10-25 15:54:55 -070029import android.app.ActivityOptions;
30import android.app.IApplicationThread;
Bryce Lee93e7f792017-10-25 15:54:55 -070031import android.content.Intent;
32import android.content.pm.ActivityInfo;
Bryce Leead5b8322018-03-08 14:28:52 -080033import android.content.pm.ActivityInfo.WindowLayout;
Bryce Lee93e7f792017-10-25 15:54:55 -070034import android.content.pm.ApplicationInfo;
35import android.content.pm.IPackageManager;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070036import android.graphics.Rect;
Bryce Lee93e7f792017-10-25 15:54:55 -070037import android.os.IBinder;
38import android.os.RemoteException;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070039import android.platform.test.annotations.Presubmit;
Bryce Lee93e7f792017-10-25 15:54:55 -070040import android.service.voice.IVoiceInteractionSession;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070041import android.support.test.filters.SmallTest;
42import android.support.test.runner.AndroidJUnit4;
Bryce Leead5b8322018-03-08 14:28:52 -080043import android.view.Gravity;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070044
45import org.junit.runner.RunWith;
46import org.junit.Test;
47
48import static com.android.server.am.ActivityManagerService.ANIMATE;
49
50import static org.junit.Assert.assertEquals;
51import static org.junit.Assert.assertTrue;
52import static org.mockito.Mockito.any;
53import static org.mockito.Mockito.anyBoolean;
54import static org.mockito.Mockito.anyInt;
Bryce Lee93e7f792017-10-25 15:54:55 -070055import static org.mockito.Mockito.anyObject;
56import static org.mockito.Mockito.doAnswer;
Bryce Leead5b8322018-03-08 14:28:52 -080057import static org.mockito.Mockito.doNothing;
Bryce Lee93e7f792017-10-25 15:54:55 -070058import static org.mockito.Mockito.doReturn;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070059import static org.mockito.Mockito.eq;
Bryce Lee93e7f792017-10-25 15:54:55 -070060import static org.mockito.Mockito.mock;
61import static org.mockito.Mockito.spy;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070062import static org.mockito.Mockito.verify;
63import static org.mockito.Mockito.times;
64
Bryce Lee93e7f792017-10-25 15:54:55 -070065import static android.app.ActivityManager.START_PERMISSION_DENIED;
66import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
67
68import com.android.internal.os.BatteryStatsImpl;
Bryce Leedaa91e42017-12-06 14:13:01 -080069import com.android.server.am.ActivityStarter.Factory;
Bryce Leead5b8322018-03-08 14:28:52 -080070import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
71import com.android.server.am.TaskRecord.TaskRecordFactory;
Bryce Lee93e7f792017-10-25 15:54:55 -070072
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070073/**
Bryce Leed3624e12017-11-30 08:51:45 -080074 * Tests for the {@link ActivityStarter} class.
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070075 *
76 * Build/Install/Run:
Bryce Leed3624e12017-11-30 08:51:45 -080077 * atest FrameworksServicesTests:ActivityStarterTests
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070078 */
79@SmallTest
80@Presubmit
81@RunWith(AndroidJUnit4.class)
82public class ActivityStarterTests extends ActivityTestsBase {
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070083 private ActivityManagerService mService;
84 private ActivityStarter mStarter;
Bryce Leed3624e12017-11-30 08:51:45 -080085 private ActivityStartController mController;
Bryce Lee93e7f792017-10-25 15:54:55 -070086
87 private static final int PRECONDITION_NO_CALLER_APP = 1;
88 private static final int PRECONDITION_NO_INTENT_COMPONENT = 1 << 1;
89 private static final int PRECONDITION_NO_ACTIVITY_INFO = 1 << 2;
90 private static final int PRECONDITION_SOURCE_PRESENT = 1 << 3;
91 private static final int PRECONDITION_REQUEST_CODE = 1 << 4;
92 private static final int PRECONDITION_SOURCE_VOICE_SESSION = 1 << 5;
93 private static final int PRECONDITION_NO_VOICE_SESSION_SUPPORT = 1 << 6;
94 private static final int PRECONDITION_DIFFERENT_UID = 1 << 7;
95 private static final int PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION = 1 << 8;
96 private static final int PRECONDITION_CANNOT_START_ANY_ACTIVITY = 1 << 9;
97 private static final int PRECONDITION_DISALLOW_APP_SWITCHING = 1 << 10;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070098
99 @Override
100 public void setUp() throws Exception {
101 super.setUp();
102 mService = createActivityManagerService();
Bryce Leed3624e12017-11-30 08:51:45 -0800103 mController = mock(ActivityStartController.class);
104 mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor,
105 mock(ActivityStartInterceptor.class));
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700106 }
107
108 @Test
109 public void testUpdateLaunchBounds() throws Exception {
110 // When in a non-resizeable stack, the task bounds should be updated.
Bryce Lee18d51592017-10-25 10:22:19 -0700111 final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
112 .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
113 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
114 .build();
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700115 final Rect bounds = new Rect(10, 10, 100, 100);
116
117 mStarter.updateBounds(task, bounds);
Bryce Leef3c6a472017-11-14 14:53:06 -0800118 assertEquals(task.getOverrideBounds(), bounds);
119 assertEquals(new Rect(), task.getStack().getOverrideBounds());
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700120
121 // When in a resizeable stack, the stack bounds should be updated as well.
Bryce Lee18d51592017-10-25 10:22:19 -0700122 final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
123 .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
124 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
125 .build();
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700126 assertTrue(task2.getStack() instanceof PinnedActivityStack);
127 mStarter.updateBounds(task2, bounds);
128
Wale Ogunwale44f036f2017-09-29 05:09:09 -0700129 verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700130 eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
131
132 // In the case of no animation, the stack and task bounds should be set immediately.
133 if (!ANIMATE) {
Bryce Leef3c6a472017-11-14 14:53:06 -0800134 assertEquals(task2.getStack().getOverrideBounds(), bounds);
135 assertEquals(task2.getOverrideBounds(), bounds);
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700136 } else {
Bryce Leef3c6a472017-11-14 14:53:06 -0800137 assertEquals(task2.getOverrideBounds(), new Rect());
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700138 }
139 }
Bryce Lee93e7f792017-10-25 15:54:55 -0700140
141 @Test
142 public void testStartActivityPreconditions() throws Exception {
143 verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED);
144 verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT,
145 START_INTENT_NOT_RESOLVED);
146 verifyStartActivityPreconditions(PRECONDITION_NO_ACTIVITY_INFO, START_CLASS_NOT_FOUND);
147 verifyStartActivityPreconditions(PRECONDITION_SOURCE_PRESENT | PRECONDITION_REQUEST_CODE,
148 Intent.FLAG_ACTIVITY_FORWARD_RESULT, START_FORWARD_AND_REQUEST_CONFLICT);
149 verifyStartActivityPreconditions(
150 PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
151 | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID,
152 START_NOT_VOICE_COMPATIBLE);
153 verifyStartActivityPreconditions(
154 PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
155 | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID
156 | PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION,
157 START_NOT_VOICE_COMPATIBLE);
158 verifyStartActivityPreconditions(PRECONDITION_CANNOT_START_ANY_ACTIVITY, START_ABORTED);
159 verifyStartActivityPreconditions(PRECONDITION_DISALLOW_APP_SWITCHING,
160 START_SWITCHES_CANCELED);
161 }
162
163 private static boolean containsConditions(int preconditions, int mask) {
164 return (preconditions & mask) == mask;
165 }
166
167 private void verifyStartActivityPreconditions(int preconditions, int expectedResult) {
168 verifyStartActivityPreconditions(preconditions, 0 /*launchFlags*/, expectedResult);
169 }
170
171 /**
172 * Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
173 * provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
174 * and the launch flags specified in the intent. The method constructs a call to
Bryce Lee4c9a5972017-12-01 22:14:24 -0800175 * {@link ActivityStarter#execute} based on these preconditions and ensures the result matches
176 * the expected. It is important to note that the method also checks side effects of the start,
177 * such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios.
Bryce Lee93e7f792017-10-25 15:54:55 -0700178 * @param preconditions A bitmask representing the preconditions for the launch
179 * @param launchFlags The launch flags to be provided by the launch {@link Intent}.
180 * @param expectedResult The expected result from the launch.
181 */
182 private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
183 int expectedResult) {
184 final ActivityManagerService service = createActivityManagerService();
185 final IPackageManager packageManager = mock(IPackageManager.class);
Bryce Leed3624e12017-11-30 08:51:45 -0800186 final ActivityStartController controller = mock(ActivityStartController.class);
Bryce Lee93e7f792017-10-25 15:54:55 -0700187
Bryce Leed3624e12017-11-30 08:51:45 -0800188 final ActivityStarter starter = new ActivityStarter(controller, service,
189 service.mStackSupervisor, mock(ActivityStartInterceptor.class));
Bryce Lee93e7f792017-10-25 15:54:55 -0700190 final IApplicationThread caller = mock(IApplicationThread.class);
191
192 // If no caller app, return {@code null} {@link ProcessRecord}.
193 final ProcessRecord record = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
194 ? null : new ProcessRecord(mock(BatteryStatsImpl.class),
195 mock(ApplicationInfo.class), null, 0);
196
197 doReturn(record).when(service).getRecordForAppLocked(anyObject());
198
199 final Intent intent = new Intent();
200 intent.setFlags(launchFlags);
201
202 final ActivityInfo aInfo = containsConditions(preconditions, PRECONDITION_NO_ACTIVITY_INFO)
203 ? null : new ActivityInfo();
204
Bryce Lee93e7f792017-10-25 15:54:55 -0700205 IVoiceInteractionSession voiceSession =
206 containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
207 ? mock(IVoiceInteractionSession.class) : null;
208
209 // Create source token
210 final ActivityBuilder builder = new ActivityBuilder(service).setTask(
211 new TaskBuilder(service.mStackSupervisor).setVoiceSession(voiceSession).build());
212
Bryce Leefbd263b42018-03-07 10:33:55 -0800213 if (aInfo != null) {
214 aInfo.applicationInfo = new ApplicationInfo();
Bryce Leead5b8322018-03-08 14:28:52 -0800215 aInfo.applicationInfo.packageName =
216 ActivityBuilder.getDefaultComponent().getPackageName();
Bryce Leefbd263b42018-03-07 10:33:55 -0800217 }
218
Bryce Lee93e7f792017-10-25 15:54:55 -0700219 // Offset uid by one from {@link ActivityInfo} to simulate different uids.
220 if (containsConditions(preconditions, PRECONDITION_DIFFERENT_UID)) {
221 builder.setUid(aInfo.applicationInfo.uid + 1);
222 }
223
224 final ActivityRecord source = builder.build();
225
226 if (!containsConditions(preconditions, PRECONDITION_NO_INTENT_COMPONENT)) {
227 intent.setComponent(source.realActivity);
228 }
229
230 if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
231 doReturn(false).when(service).checkAppSwitchAllowedLocked(anyInt(), anyInt(), anyInt(),
232 anyInt(), any());
233 }
234
235 if (containsConditions(preconditions,PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
236 doReturn(false).when(service.mStackSupervisor).checkStartAnyActivityPermission(
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100237 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
238 anyBoolean(), any(), any(), any());
Bryce Lee93e7f792017-10-25 15:54:55 -0700239 }
240
241 try {
242 if (containsConditions(preconditions,
243 PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION)) {
244 doAnswer((inv) -> {
245 throw new RemoteException();
246 }).when(packageManager).activitySupportsIntent(eq(source.realActivity), eq(intent),
247 any());
248 } else {
249 doReturn(!containsConditions(preconditions, PRECONDITION_NO_VOICE_SESSION_SUPPORT))
250 .when(packageManager).activitySupportsIntent(eq(source.realActivity),
251 eq(intent), any());
252 }
253 } catch (RemoteException e) {
254 }
255
256 final IBinder resultTo = containsConditions(preconditions, PRECONDITION_SOURCE_PRESENT)
257 || containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
258 ? source.appToken : null;
259
260 final int requestCode = containsConditions(preconditions, PRECONDITION_REQUEST_CODE)
261 ? 1 : 0;
262
Bryce Lee4c9a5972017-12-01 22:14:24 -0800263 final int result = starter.setCaller(caller)
264 .setIntent(intent)
265 .setActivityInfo(aInfo)
266 .setResultTo(resultTo)
267 .setRequestCode(requestCode)
268 .setReason("testLaunchActivityPermissionDenied")
269 .execute();
Bryce Lee93e7f792017-10-25 15:54:55 -0700270
271 // In some cases the expected result internally is different than the published result. We
272 // must use ActivityStarter#getExternalResult to translate.
273 assertEquals(ActivityStarter.getExternalResult(expectedResult), result);
274
275 // Ensure that {@link ActivityOptions} are aborted with unsuccessful result.
276 if (expectedResult != START_SUCCESS) {
Bryce Lee4c9a5972017-12-01 22:14:24 -0800277 final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
278 mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
Bryce Lee93e7f792017-10-25 15:54:55 -0700279 final ActivityOptions options = spy(ActivityOptions.makeBasic());
Bryce Lee4c9a5972017-12-01 22:14:24 -0800280
281 final int optionResult = optionStarter.setCaller(caller)
282 .setIntent(intent)
283 .setActivityInfo(aInfo)
284 .setResultTo(resultTo)
285 .setRequestCode(requestCode)
286 .setReason("testLaunchActivityPermissionDenied")
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100287 .setActivityOptions(new SafeActivityOptions(options))
Bryce Lee4c9a5972017-12-01 22:14:24 -0800288 .execute();
Bryce Lee93e7f792017-10-25 15:54:55 -0700289 verify(options, times(1)).abort();
290 }
291 }
Bryce Leeb802ea12017-11-15 21:25:03 -0800292
Bryce Leead5b8322018-03-08 14:28:52 -0800293 private ActivityStarter prepareStarter() {
294 // always allow test to start activity.
295 doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
296 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
297 anyBoolean(), any(), any(), any());
298
299 // instrument the stack and task used.
300 final ActivityStack stack = spy(mService.mStackSupervisor.getDefaultDisplay().createStack(
301 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */));
302 final TaskRecord task =
303 spy(new TaskBuilder(mService.mStackSupervisor).setStack(stack).build());
304
305 // supervisor needs a focused stack.
306 mService.mStackSupervisor.mFocusedStack = task.getStack();
307
308 // use factory that only returns spy task.
309 final TaskRecordFactory factory = mock(TaskRecordFactory.class);
310 TaskRecord.setTaskRecordFactory(factory);
311
312 // return task when created.
313 doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any());
314
315 // direct starter to use spy stack.
316 doReturn(stack).when(mService.mStackSupervisor)
317 .getLaunchStack(any(), any(), any(), anyBoolean());
318 doReturn(stack).when(mService.mStackSupervisor)
319 .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
320
321 // ignore the start request.
322 doNothing().when(stack)
323 .startActivityLocked(any(), any(), anyBoolean(), anyBoolean(), any());
324
325 // ignore requests to create window container.
326 doNothing().when(task).createWindowContainer(anyBoolean(), anyBoolean());
327
328 return new ActivityStarter(mController, mService,
329 mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
330 }
331
332 /**
333 * Ensures that values specified at launch time are passed to {@link LaunchParamsModifier}
334 * when we are laying out a new task.
335 */
336 @Test
337 public void testCreateTaskLayout() {
338 // modifier for validating passed values.
339 final LaunchParamsModifier modifier = mock(LaunchParamsModifier.class);
340 mService.mStackSupervisor.getLaunchParamsController().registerModifier(modifier);
341
342 // add custom values to activity info to make unique.
343 final ActivityInfo info = new ActivityInfo();
344 final Rect launchBounds = new Rect(0, 0, 20, 30);
345 final Intent intent = new Intent();
346
347 intent.setComponent(ActivityBuilder.getDefaultComponent());
348
349 final WindowLayout windowLayout =
350 new WindowLayout(10, .5f, 20, 1.0f, Gravity.NO_GRAVITY, 1, 1);
351
352 info.windowLayout = windowLayout;
353 info.applicationInfo = new ApplicationInfo();
354 info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
355
356 // create starter.
357 final ActivityStarter optionStarter = prepareStarter();
358
359 final ActivityOptions options = ActivityOptions.makeBasic();
360 options.setLaunchBounds(launchBounds);
361
362 // run starter.
363 optionStarter
364 .setIntent(intent)
365 .setReason("testCreateTaskLayout")
366 .setActivityInfo(info)
367 .setActivityOptions(new SafeActivityOptions(options))
368 .execute();
369
370 // verify that values are passed to the modifier.
371 verify(modifier, times(1)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
372 any(), any());
373 }
Wale Ogunwale44f036f2017-09-29 05:09:09 -0700374}