blob: b58c7003f39e353343433b050f101294c8a0130d [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;
33import android.content.pm.ApplicationInfo;
34import android.content.pm.IPackageManager;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070035import android.graphics.Rect;
Bryce Lee93e7f792017-10-25 15:54:55 -070036import android.os.IBinder;
37import android.os.RemoteException;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070038import android.platform.test.annotations.Presubmit;
Bryce Lee93e7f792017-10-25 15:54:55 -070039import android.service.voice.IVoiceInteractionSession;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070040import android.support.test.filters.SmallTest;
41import android.support.test.runner.AndroidJUnit4;
42
43import org.junit.runner.RunWith;
44import org.junit.Test;
45
46import static com.android.server.am.ActivityManagerService.ANIMATE;
47
48import static org.junit.Assert.assertEquals;
49import static org.junit.Assert.assertTrue;
50import static org.mockito.Mockito.any;
51import static org.mockito.Mockito.anyBoolean;
52import static org.mockito.Mockito.anyInt;
Bryce Lee93e7f792017-10-25 15:54:55 -070053import static org.mockito.Mockito.anyObject;
54import static org.mockito.Mockito.doAnswer;
55import static org.mockito.Mockito.doReturn;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070056import static org.mockito.Mockito.eq;
Bryce Lee93e7f792017-10-25 15:54:55 -070057import static org.mockito.Mockito.mock;
58import static org.mockito.Mockito.spy;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070059import static org.mockito.Mockito.verify;
60import static org.mockito.Mockito.times;
61
Bryce Lee93e7f792017-10-25 15:54:55 -070062import static android.app.ActivityManager.START_PERMISSION_DENIED;
63import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
64
65import com.android.internal.os.BatteryStatsImpl;
Bryce Leedaa91e42017-12-06 14:13:01 -080066import com.android.server.am.ActivityStarter.Factory;
Bryce Lee93e7f792017-10-25 15:54:55 -070067
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070068/**
Bryce Leed3624e12017-11-30 08:51:45 -080069 * Tests for the {@link ActivityStarter} class.
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070070 *
71 * Build/Install/Run:
Bryce Leed3624e12017-11-30 08:51:45 -080072 * atest FrameworksServicesTests:ActivityStarterTests
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070073 */
74@SmallTest
75@Presubmit
76@RunWith(AndroidJUnit4.class)
77public class ActivityStarterTests extends ActivityTestsBase {
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070078 private ActivityManagerService mService;
79 private ActivityStarter mStarter;
Bryce Leed3624e12017-11-30 08:51:45 -080080 private ActivityStartController mController;
Bryce Lee93e7f792017-10-25 15:54:55 -070081
82 private static final int PRECONDITION_NO_CALLER_APP = 1;
83 private static final int PRECONDITION_NO_INTENT_COMPONENT = 1 << 1;
84 private static final int PRECONDITION_NO_ACTIVITY_INFO = 1 << 2;
85 private static final int PRECONDITION_SOURCE_PRESENT = 1 << 3;
86 private static final int PRECONDITION_REQUEST_CODE = 1 << 4;
87 private static final int PRECONDITION_SOURCE_VOICE_SESSION = 1 << 5;
88 private static final int PRECONDITION_NO_VOICE_SESSION_SUPPORT = 1 << 6;
89 private static final int PRECONDITION_DIFFERENT_UID = 1 << 7;
90 private static final int PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION = 1 << 8;
91 private static final int PRECONDITION_CANNOT_START_ANY_ACTIVITY = 1 << 9;
92 private static final int PRECONDITION_DISALLOW_APP_SWITCHING = 1 << 10;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070093
94 @Override
95 public void setUp() throws Exception {
96 super.setUp();
97 mService = createActivityManagerService();
Bryce Leed3624e12017-11-30 08:51:45 -080098 mController = mock(ActivityStartController.class);
99 mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor,
100 mock(ActivityStartInterceptor.class));
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700101 }
102
103 @Test
104 public void testUpdateLaunchBounds() throws Exception {
105 // When in a non-resizeable stack, the task bounds should be updated.
Bryce Lee18d51592017-10-25 10:22:19 -0700106 final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
107 .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
108 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
109 .build();
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700110 final Rect bounds = new Rect(10, 10, 100, 100);
111
112 mStarter.updateBounds(task, bounds);
Bryce Leef3c6a472017-11-14 14:53:06 -0800113 assertEquals(task.getOverrideBounds(), bounds);
114 assertEquals(new Rect(), task.getStack().getOverrideBounds());
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700115
116 // When in a resizeable stack, the stack bounds should be updated as well.
Bryce Lee18d51592017-10-25 10:22:19 -0700117 final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
118 .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
119 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
120 .build();
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700121 assertTrue(task2.getStack() instanceof PinnedActivityStack);
122 mStarter.updateBounds(task2, bounds);
123
Wale Ogunwale44f036f2017-09-29 05:09:09 -0700124 verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700125 eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
126
127 // In the case of no animation, the stack and task bounds should be set immediately.
128 if (!ANIMATE) {
Bryce Leef3c6a472017-11-14 14:53:06 -0800129 assertEquals(task2.getStack().getOverrideBounds(), bounds);
130 assertEquals(task2.getOverrideBounds(), bounds);
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700131 } else {
Bryce Leef3c6a472017-11-14 14:53:06 -0800132 assertEquals(task2.getOverrideBounds(), new Rect());
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700133 }
134 }
Bryce Lee93e7f792017-10-25 15:54:55 -0700135
136 @Test
137 public void testStartActivityPreconditions() throws Exception {
138 verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED);
139 verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT,
140 START_INTENT_NOT_RESOLVED);
141 verifyStartActivityPreconditions(PRECONDITION_NO_ACTIVITY_INFO, START_CLASS_NOT_FOUND);
142 verifyStartActivityPreconditions(PRECONDITION_SOURCE_PRESENT | PRECONDITION_REQUEST_CODE,
143 Intent.FLAG_ACTIVITY_FORWARD_RESULT, START_FORWARD_AND_REQUEST_CONFLICT);
144 verifyStartActivityPreconditions(
145 PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
146 | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID,
147 START_NOT_VOICE_COMPATIBLE);
148 verifyStartActivityPreconditions(
149 PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
150 | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID
151 | PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION,
152 START_NOT_VOICE_COMPATIBLE);
153 verifyStartActivityPreconditions(PRECONDITION_CANNOT_START_ANY_ACTIVITY, START_ABORTED);
154 verifyStartActivityPreconditions(PRECONDITION_DISALLOW_APP_SWITCHING,
155 START_SWITCHES_CANCELED);
156 }
157
158 private static boolean containsConditions(int preconditions, int mask) {
159 return (preconditions & mask) == mask;
160 }
161
162 private void verifyStartActivityPreconditions(int preconditions, int expectedResult) {
163 verifyStartActivityPreconditions(preconditions, 0 /*launchFlags*/, expectedResult);
164 }
165
166 /**
167 * Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
168 * provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
169 * and the launch flags specified in the intent. The method constructs a call to
Bryce Lee4c9a5972017-12-01 22:14:24 -0800170 * {@link ActivityStarter#execute} based on these preconditions and ensures the result matches
171 * the expected. It is important to note that the method also checks side effects of the start,
172 * such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios.
Bryce Lee93e7f792017-10-25 15:54:55 -0700173 * @param preconditions A bitmask representing the preconditions for the launch
174 * @param launchFlags The launch flags to be provided by the launch {@link Intent}.
175 * @param expectedResult The expected result from the launch.
176 */
177 private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
178 int expectedResult) {
179 final ActivityManagerService service = createActivityManagerService();
180 final IPackageManager packageManager = mock(IPackageManager.class);
Bryce Leed3624e12017-11-30 08:51:45 -0800181 final ActivityStartController controller = mock(ActivityStartController.class);
Bryce Lee93e7f792017-10-25 15:54:55 -0700182
Bryce Leed3624e12017-11-30 08:51:45 -0800183 final ActivityStarter starter = new ActivityStarter(controller, service,
184 service.mStackSupervisor, mock(ActivityStartInterceptor.class));
Bryce Lee93e7f792017-10-25 15:54:55 -0700185 final IApplicationThread caller = mock(IApplicationThread.class);
186
187 // If no caller app, return {@code null} {@link ProcessRecord}.
188 final ProcessRecord record = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
189 ? null : new ProcessRecord(mock(BatteryStatsImpl.class),
190 mock(ApplicationInfo.class), null, 0);
191
192 doReturn(record).when(service).getRecordForAppLocked(anyObject());
193
194 final Intent intent = new Intent();
195 intent.setFlags(launchFlags);
196
197 final ActivityInfo aInfo = containsConditions(preconditions, PRECONDITION_NO_ACTIVITY_INFO)
198 ? null : new ActivityInfo();
199
200 if (aInfo != null) {
201 aInfo.applicationInfo = new ApplicationInfo();
202 aInfo.applicationInfo.packageName = ActivityBuilder.DEFAULT_PACKAGE;
203 }
204
205 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
213 // Offset uid by one from {@link ActivityInfo} to simulate different uids.
214 if (containsConditions(preconditions, PRECONDITION_DIFFERENT_UID)) {
215 builder.setUid(aInfo.applicationInfo.uid + 1);
216 }
217
218 final ActivityRecord source = builder.build();
219
220 if (!containsConditions(preconditions, PRECONDITION_NO_INTENT_COMPONENT)) {
221 intent.setComponent(source.realActivity);
222 }
223
224 if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
225 doReturn(false).when(service).checkAppSwitchAllowedLocked(anyInt(), anyInt(), anyInt(),
226 anyInt(), any());
227 }
228
229 if (containsConditions(preconditions,PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
230 doReturn(false).when(service.mStackSupervisor).checkStartAnyActivityPermission(
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100231 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
232 anyBoolean(), any(), any(), any());
Bryce Lee93e7f792017-10-25 15:54:55 -0700233 }
234
235 try {
236 if (containsConditions(preconditions,
237 PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION)) {
238 doAnswer((inv) -> {
239 throw new RemoteException();
240 }).when(packageManager).activitySupportsIntent(eq(source.realActivity), eq(intent),
241 any());
242 } else {
243 doReturn(!containsConditions(preconditions, PRECONDITION_NO_VOICE_SESSION_SUPPORT))
244 .when(packageManager).activitySupportsIntent(eq(source.realActivity),
245 eq(intent), any());
246 }
247 } catch (RemoteException e) {
248 }
249
250 final IBinder resultTo = containsConditions(preconditions, PRECONDITION_SOURCE_PRESENT)
251 || containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
252 ? source.appToken : null;
253
254 final int requestCode = containsConditions(preconditions, PRECONDITION_REQUEST_CODE)
255 ? 1 : 0;
256
Bryce Lee4c9a5972017-12-01 22:14:24 -0800257 final int result = starter.setCaller(caller)
258 .setIntent(intent)
259 .setActivityInfo(aInfo)
260 .setResultTo(resultTo)
261 .setRequestCode(requestCode)
262 .setReason("testLaunchActivityPermissionDenied")
263 .execute();
Bryce Lee93e7f792017-10-25 15:54:55 -0700264
265 // In some cases the expected result internally is different than the published result. We
266 // must use ActivityStarter#getExternalResult to translate.
267 assertEquals(ActivityStarter.getExternalResult(expectedResult), result);
268
269 // Ensure that {@link ActivityOptions} are aborted with unsuccessful result.
270 if (expectedResult != START_SUCCESS) {
Bryce Lee4c9a5972017-12-01 22:14:24 -0800271 final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
272 mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
Bryce Lee93e7f792017-10-25 15:54:55 -0700273 final ActivityOptions options = spy(ActivityOptions.makeBasic());
Bryce Lee4c9a5972017-12-01 22:14:24 -0800274
275 final int optionResult = optionStarter.setCaller(caller)
276 .setIntent(intent)
277 .setActivityInfo(aInfo)
278 .setResultTo(resultTo)
279 .setRequestCode(requestCode)
280 .setReason("testLaunchActivityPermissionDenied")
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100281 .setActivityOptions(new SafeActivityOptions(options))
Bryce Lee4c9a5972017-12-01 22:14:24 -0800282 .execute();
Bryce Lee93e7f792017-10-25 15:54:55 -0700283 verify(options, times(1)).abort();
284 }
285 }
Bryce Leeb802ea12017-11-15 21:25:03 -0800286
287// TODO(b/69270257): Add test to verify task layout is passed additional data such as activity and
288// source.
289// @Test
290// public void testCreateTaskLayout() {
291// }
Wale Ogunwale44f036f2017-09-29 05:09:09 -0700292}