blob: f7d7ad6d986ff0023e22aef1c5adeb3a0851480b [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
Bryce Lee93e7f792017-10-25 15:54:55 -070019import static android.app.ActivityManager.START_ABORTED;
20import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
Bryce Lee32e09ef2018-03-19 15:29:49 -070021import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
Bryce Lee93e7f792017-10-25 15:54:55 -070022import static android.app.ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
Bryce Lee32e09ef2018-03-19 15:29:49 -070023import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
Bryce Lee93e7f792017-10-25 15:54:55 -070024import static android.app.ActivityManager.START_NOT_VOICE_COMPATIBLE;
Bryce Lee32e09ef2018-03-19 15:29:49 -070025import static android.app.ActivityManager.START_PERMISSION_DENIED;
Bryce Lee2b8e0372018-04-05 17:01:37 -070026import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
Bryce Lee93e7f792017-10-25 15:54:55 -070027import static android.app.ActivityManager.START_SUCCESS;
28import static android.app.ActivityManager.START_SWITCHES_CANCELED;
Bryce Lee32e09ef2018-03-19 15:29:49 -070029import static android.app.ActivityManager.START_TASK_TO_FRONT;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070030import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
31import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
32import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
Bryce Lee32e09ef2018-03-19 15:29:49 -070033import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
34import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
Riddle Hsub70b36d2018-09-11 21:20:02 +080035import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
Brett Chabota26eda92018-07-23 13:08:30 -070036import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
Riddle Hsub70b36d2018-09-11 21:20:02 +080037import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
Brett Chabota26eda92018-07-23 13:08:30 -070038
Wale Ogunwale59507092018-10-29 09:00:30 -070039import static com.android.server.wm.ActivityDisplay.POSITION_BOTTOM;
40import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
41import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
Brett Chabota26eda92018-07-23 13:08:30 -070042
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +090043import static com.google.common.truth.Truth.assertThat;
44
Brett Chabota26eda92018-07-23 13:08:30 -070045import static org.junit.Assert.assertEquals;
Riddle Hsub70b36d2018-09-11 21:20:02 +080046import static org.mockito.ArgumentMatchers.any;
47import static org.mockito.ArgumentMatchers.anyBoolean;
48import static org.mockito.ArgumentMatchers.anyInt;
49import static org.mockito.ArgumentMatchers.anyObject;
50import static org.mockito.ArgumentMatchers.eq;
51import static org.mockito.Mockito.clearInvocations;
Brett Chabota26eda92018-07-23 13:08:30 -070052import static org.mockito.Mockito.doAnswer;
Wale Ogunwale342fbe92018-10-09 08:44:10 -070053import static org.mockito.Mockito.doNothing;
Brett Chabota26eda92018-07-23 13:08:30 -070054import static org.mockito.Mockito.doReturn;
Brett Chabota26eda92018-07-23 13:08:30 -070055import static org.mockito.Mockito.mock;
Riddle Hsub70b36d2018-09-11 21:20:02 +080056import static org.mockito.Mockito.never;
Brett Chabota26eda92018-07-23 13:08:30 -070057import static org.mockito.Mockito.spy;
58import static org.mockito.Mockito.times;
59import static org.mockito.Mockito.verify;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070060
Bryce Lee93e7f792017-10-25 15:54:55 -070061import android.app.ActivityOptions;
62import android.app.IApplicationThread;
Riddle Hsub70b36d2018-09-11 21:20:02 +080063import android.content.ComponentName;
Bryce Lee93e7f792017-10-25 15:54:55 -070064import android.content.Intent;
65import android.content.pm.ActivityInfo;
Bryce Leead5b8322018-03-08 14:28:52 -080066import android.content.pm.ActivityInfo.WindowLayout;
Bryce Lee93e7f792017-10-25 15:54:55 -070067import android.content.pm.ApplicationInfo;
68import android.content.pm.IPackageManager;
Philip P. Moltmann6c644e62018-07-18 15:41:24 -070069import android.content.pm.PackageManagerInternal;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070070import android.graphics.Rect;
Bryce Lee93e7f792017-10-25 15:54:55 -070071import android.os.IBinder;
72import android.os.RemoteException;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070073import android.platform.test.annotations.Presubmit;
Bryce Lee93e7f792017-10-25 15:54:55 -070074import android.service.voice.IVoiceInteractionSession;
Bryce Leead5b8322018-03-08 14:28:52 -080075import android.view.Gravity;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070076
Brett Chabota26eda92018-07-23 13:08:30 -070077import androidx.test.filters.SmallTest;
Brett Chabota26eda92018-07-23 13:08:30 -070078
Wale Ogunwale59507092018-10-29 09:00:30 -070079import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
80import com.android.server.wm.TaskRecord.TaskRecordFactory;
Bryce Lee93e7f792017-10-25 15:54:55 -070081
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +090082import org.junit.Before;
Brett Chabota26eda92018-07-23 13:08:30 -070083import org.junit.Test;
Bryce Lee2b8e0372018-04-05 17:01:37 -070084
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070085/**
Bryce Leed3624e12017-11-30 08:51:45 -080086 * Tests for the {@link ActivityStarter} class.
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070087 *
88 * Build/Install/Run:
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +090089 * atest WmTests:ActivityStarterTests
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070090 */
91@SmallTest
92@Presubmit
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070093public class ActivityStarterTests extends ActivityTestsBase {
Bryce Lee4e4a3ec2017-09-27 08:25:03 -070094 private ActivityStarter mStarter;
Bryce Leed3624e12017-11-30 08:51:45 -080095 private ActivityStartController mController;
Michal Karpinski201bc0c2018-07-20 15:32:00 +010096 private ActivityMetricsLogger mActivityMetricsLogger;
Bryce Lee93e7f792017-10-25 15:54:55 -070097
98 private static final int PRECONDITION_NO_CALLER_APP = 1;
99 private static final int PRECONDITION_NO_INTENT_COMPONENT = 1 << 1;
100 private static final int PRECONDITION_NO_ACTIVITY_INFO = 1 << 2;
101 private static final int PRECONDITION_SOURCE_PRESENT = 1 << 3;
102 private static final int PRECONDITION_REQUEST_CODE = 1 << 4;
103 private static final int PRECONDITION_SOURCE_VOICE_SESSION = 1 << 5;
104 private static final int PRECONDITION_NO_VOICE_SESSION_SUPPORT = 1 << 6;
105 private static final int PRECONDITION_DIFFERENT_UID = 1 << 7;
106 private static final int PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION = 1 << 8;
107 private static final int PRECONDITION_CANNOT_START_ANY_ACTIVITY = 1 << 9;
108 private static final int PRECONDITION_DISALLOW_APP_SWITCHING = 1 << 10;
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700109
Michal Karpinski201bc0c2018-07-20 15:32:00 +0100110 private static final int FAKE_CALLING_UID = 666;
111 private static final int FAKE_REAL_CALLING_UID = 667;
112 private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude";
113
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900114 @Before
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700115 public void setUp() throws Exception {
Riddle Hsub70b36d2018-09-11 21:20:02 +0800116 setupActivityTaskManagerService();
Bryce Leed3624e12017-11-30 08:51:45 -0800117 mController = mock(ActivityStartController.class);
Michal Karpinski201bc0c2018-07-20 15:32:00 +0100118 mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
119 clearInvocations(mActivityMetricsLogger);
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700120 mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor,
Bryce Leed3624e12017-11-30 08:51:45 -0800121 mock(ActivityStartInterceptor.class));
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700122 }
123
124 @Test
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900125 public void testUpdateLaunchBounds() {
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700126 // When in a non-resizeable stack, the task bounds should be updated.
Bryce Lee18d51592017-10-25 10:22:19 -0700127 final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
128 .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
129 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
130 .build();
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700131 final Rect bounds = new Rect(10, 10, 100, 100);
132
133 mStarter.updateBounds(task, bounds);
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900134 assertEquals(bounds, task.getOverrideBounds());
Bryce Leef3c6a472017-11-14 14:53:06 -0800135 assertEquals(new Rect(), task.getStack().getOverrideBounds());
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700136
137 // When in a resizeable stack, the stack bounds should be updated as well.
Bryce Lee18d51592017-10-25 10:22:19 -0700138 final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
139 .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
140 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
141 .build();
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900142 assertThat((Object) task2.getStack()).isInstanceOf(PinnedActivityStack.class);
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700143 mStarter.updateBounds(task2, bounds);
144
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700145 verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700146 eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
147
148 // In the case of no animation, the stack and task bounds should be set immediately.
149 if (!ANIMATE) {
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900150 assertEquals(bounds, task2.getStack().getOverrideBounds());
151 assertEquals(bounds, task2.getOverrideBounds());
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700152 } else {
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900153 assertEquals(new Rect(), task2.getOverrideBounds());
Bryce Lee4e4a3ec2017-09-27 08:25:03 -0700154 }
155 }
Bryce Lee93e7f792017-10-25 15:54:55 -0700156
157 @Test
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900158 public void testStartActivityPreconditions() {
Bryce Lee93e7f792017-10-25 15:54:55 -0700159 verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED);
160 verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT,
161 START_INTENT_NOT_RESOLVED);
162 verifyStartActivityPreconditions(PRECONDITION_NO_ACTIVITY_INFO, START_CLASS_NOT_FOUND);
163 verifyStartActivityPreconditions(PRECONDITION_SOURCE_PRESENT | PRECONDITION_REQUEST_CODE,
164 Intent.FLAG_ACTIVITY_FORWARD_RESULT, START_FORWARD_AND_REQUEST_CONFLICT);
165 verifyStartActivityPreconditions(
166 PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
167 | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID,
168 START_NOT_VOICE_COMPATIBLE);
169 verifyStartActivityPreconditions(
170 PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
171 | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID
172 | PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION,
173 START_NOT_VOICE_COMPATIBLE);
174 verifyStartActivityPreconditions(PRECONDITION_CANNOT_START_ANY_ACTIVITY, START_ABORTED);
175 verifyStartActivityPreconditions(PRECONDITION_DISALLOW_APP_SWITCHING,
176 START_SWITCHES_CANCELED);
177 }
178
179 private static boolean containsConditions(int preconditions, int mask) {
180 return (preconditions & mask) == mask;
181 }
182
183 private void verifyStartActivityPreconditions(int preconditions, int expectedResult) {
184 verifyStartActivityPreconditions(preconditions, 0 /*launchFlags*/, expectedResult);
185 }
186
187 /**
188 * Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
189 * provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
190 * and the launch flags specified in the intent. The method constructs a call to
Bryce Lee4c9a5972017-12-01 22:14:24 -0800191 * {@link ActivityStarter#execute} based on these preconditions and ensures the result matches
192 * the expected. It is important to note that the method also checks side effects of the start,
193 * such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios.
Bryce Lee93e7f792017-10-25 15:54:55 -0700194 * @param preconditions A bitmask representing the preconditions for the launch
195 * @param launchFlags The launch flags to be provided by the launch {@link Intent}.
196 * @param expectedResult The expected result from the launch.
197 */
198 private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
199 int expectedResult) {
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700200 final ActivityTaskManagerService service = mService;
Bryce Lee93e7f792017-10-25 15:54:55 -0700201 final IPackageManager packageManager = mock(IPackageManager.class);
Bryce Leed3624e12017-11-30 08:51:45 -0800202 final ActivityStartController controller = mock(ActivityStartController.class);
Bryce Lee93e7f792017-10-25 15:54:55 -0700203
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700204 final ActivityStarter starter = new ActivityStarter(controller, service,
Bryce Leed3624e12017-11-30 08:51:45 -0800205 service.mStackSupervisor, mock(ActivityStartInterceptor.class));
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700206 prepareStarter(launchFlags);
Bryce Lee93e7f792017-10-25 15:54:55 -0700207 final IApplicationThread caller = mock(IApplicationThread.class);
208
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700209 final WindowProcessController wpc =
210 containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
211 ? null : new WindowProcessController(
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900212 service, mock(ApplicationInfo.class), null, 0, -1, null, null);
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700213 doReturn(wpc).when(service).getProcessController(anyObject());
Bryce Lee93e7f792017-10-25 15:54:55 -0700214
215 final Intent intent = new Intent();
216 intent.setFlags(launchFlags);
217
218 final ActivityInfo aInfo = containsConditions(preconditions, PRECONDITION_NO_ACTIVITY_INFO)
219 ? null : new ActivityInfo();
220
Bryce Lee93e7f792017-10-25 15:54:55 -0700221 IVoiceInteractionSession voiceSession =
222 containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
223 ? mock(IVoiceInteractionSession.class) : null;
224
225 // Create source token
226 final ActivityBuilder builder = new ActivityBuilder(service).setTask(
227 new TaskBuilder(service.mStackSupervisor).setVoiceSession(voiceSession).build());
228
Bryce Leefbd263b42018-03-07 10:33:55 -0800229 if (aInfo != null) {
230 aInfo.applicationInfo = new ApplicationInfo();
Bryce Leead5b8322018-03-08 14:28:52 -0800231 aInfo.applicationInfo.packageName =
232 ActivityBuilder.getDefaultComponent().getPackageName();
Bryce Leefbd263b42018-03-07 10:33:55 -0800233 }
234
Bryce Lee93e7f792017-10-25 15:54:55 -0700235 // Offset uid by one from {@link ActivityInfo} to simulate different uids.
236 if (containsConditions(preconditions, PRECONDITION_DIFFERENT_UID)) {
237 builder.setUid(aInfo.applicationInfo.uid + 1);
238 }
239
240 final ActivityRecord source = builder.build();
241
242 if (!containsConditions(preconditions, PRECONDITION_NO_INTENT_COMPONENT)) {
243 intent.setComponent(source.realActivity);
244 }
245
246 if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700247 doReturn(false).when(service).checkAppSwitchAllowedLocked(
Wale Ogunwalea6191b42018-05-09 07:41:32 -0700248 anyInt(), anyInt(), anyInt(), anyInt(), any());
Bryce Lee93e7f792017-10-25 15:54:55 -0700249 }
250
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900251 if (containsConditions(preconditions, PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
Bryce Lee93e7f792017-10-25 15:54:55 -0700252 doReturn(false).when(service.mStackSupervisor).checkStartAnyActivityPermission(
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100253 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
Winson Chungc9804e72018-05-15 11:01:44 -0700254 anyBoolean(), anyBoolean(), any(), any(), any());
Bryce Lee93e7f792017-10-25 15:54:55 -0700255 }
256
257 try {
258 if (containsConditions(preconditions,
259 PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION)) {
260 doAnswer((inv) -> {
261 throw new RemoteException();
262 }).when(packageManager).activitySupportsIntent(eq(source.realActivity), eq(intent),
263 any());
264 } else {
265 doReturn(!containsConditions(preconditions, PRECONDITION_NO_VOICE_SESSION_SUPPORT))
266 .when(packageManager).activitySupportsIntent(eq(source.realActivity),
267 eq(intent), any());
268 }
269 } catch (RemoteException e) {
270 }
271
272 final IBinder resultTo = containsConditions(preconditions, PRECONDITION_SOURCE_PRESENT)
273 || containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
274 ? source.appToken : null;
275
276 final int requestCode = containsConditions(preconditions, PRECONDITION_REQUEST_CODE)
277 ? 1 : 0;
278
Bryce Lee4c9a5972017-12-01 22:14:24 -0800279 final int result = starter.setCaller(caller)
280 .setIntent(intent)
281 .setActivityInfo(aInfo)
282 .setResultTo(resultTo)
283 .setRequestCode(requestCode)
284 .setReason("testLaunchActivityPermissionDenied")
285 .execute();
Bryce Lee93e7f792017-10-25 15:54:55 -0700286
287 // In some cases the expected result internally is different than the published result. We
288 // must use ActivityStarter#getExternalResult to translate.
289 assertEquals(ActivityStarter.getExternalResult(expectedResult), result);
290
291 // Ensure that {@link ActivityOptions} are aborted with unsuccessful result.
292 if (expectedResult != START_SUCCESS) {
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700293 final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
Bryce Lee4c9a5972017-12-01 22:14:24 -0800294 mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
Bryce Lee93e7f792017-10-25 15:54:55 -0700295 final ActivityOptions options = spy(ActivityOptions.makeBasic());
Bryce Lee4c9a5972017-12-01 22:14:24 -0800296
297 final int optionResult = optionStarter.setCaller(caller)
298 .setIntent(intent)
299 .setActivityInfo(aInfo)
300 .setResultTo(resultTo)
301 .setRequestCode(requestCode)
302 .setReason("testLaunchActivityPermissionDenied")
Jorim Jaggi4d8d32c2018-01-19 15:57:41 +0100303 .setActivityOptions(new SafeActivityOptions(options))
Bryce Lee4c9a5972017-12-01 22:14:24 -0800304 .execute();
Bryce Lee93e7f792017-10-25 15:54:55 -0700305 verify(options, times(1)).abort();
306 }
307 }
Bryce Leeb802ea12017-11-15 21:25:03 -0800308
Riddle Hsub70b36d2018-09-11 21:20:02 +0800309 private ActivityStarter prepareStarter(@Intent.Flags int launchFlags) {
310 return prepareStarter(launchFlags, true /* mockGetLaunchStack */);
311 }
312
313 /**
314 * Creates a {@link ActivityStarter} with default parameters and necessary mocks.
315 *
316 * @param launchFlags The intent flags to launch activity.
317 * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for
318 * always launching to the testing stack. Set to false when allowing
319 * the activity can be launched to any stack that is decided by real
320 * implementation.
321 * @return A {@link ActivityStarter} with default setup.
322 */
323 private ActivityStarter prepareStarter(@Intent.Flags int launchFlags,
324 boolean mockGetLaunchStack) {
Bryce Leead5b8322018-03-08 14:28:52 -0800325 // always allow test to start activity.
326 doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
327 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
Winson Chungc9804e72018-05-15 11:01:44 -0700328 anyBoolean(), anyBoolean(), any(), any(), any());
Bryce Leead5b8322018-03-08 14:28:52 -0800329
330 // instrument the stack and task used.
Bryce Lee2b8e0372018-04-05 17:01:37 -0700331 final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
332 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
333 final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
334 .setCreateStack(false)
335 .build();
Bryce Leead5b8322018-03-08 14:28:52 -0800336
Bryce Leead5b8322018-03-08 14:28:52 -0800337 // use factory that only returns spy task.
338 final TaskRecordFactory factory = mock(TaskRecordFactory.class);
339 TaskRecord.setTaskRecordFactory(factory);
340
341 // return task when created.
342 doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any());
343
Riddle Hsub70b36d2018-09-11 21:20:02 +0800344 if (mockGetLaunchStack) {
345 // Direct starter to use spy stack.
346 doReturn(stack).when(mService.mStackSupervisor)
347 .getLaunchStack(any(), any(), any(), anyBoolean());
348 doReturn(stack).when(mService.mStackSupervisor)
349 .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
350 }
Bryce Leead5b8322018-03-08 14:28:52 -0800351
Philip P. Moltmann6c644e62018-07-18 15:41:24 -0700352 // Set up mock package manager internal and make sure no unmocked methods are called
353 PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class,
354 invocation -> {
355 throw new RuntimeException("Not stubbed");
356 });
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700357 doReturn(mockPackageManager).when(mService).getPackageManagerInternalLocked();
Philip P. Moltmann6c644e62018-07-18 15:41:24 -0700358
359 // Never review permissions
360 doReturn(false).when(mockPackageManager).isPermissionsReviewRequired(any(), anyInt());
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700361 doNothing().when(mockPackageManager).grantEphemeralAccess(
362 anyInt(), any(), anyInt(), anyInt());
Philip P. Moltmann6c644e62018-07-18 15:41:24 -0700363
Bryce Lee32e09ef2018-03-19 15:29:49 -0700364 final Intent intent = new Intent();
365 intent.addFlags(launchFlags);
366 intent.setComponent(ActivityBuilder.getDefaultComponent());
367
368 final ActivityInfo info = new ActivityInfo();
369
370 info.applicationInfo = new ApplicationInfo();
371 info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
372
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700373 return new ActivityStarter(mController, mService,
Bryce Lee32e09ef2018-03-19 15:29:49 -0700374 mService.mStackSupervisor, mock(ActivityStartInterceptor.class))
375 .setIntent(intent)
376 .setActivityInfo(info);
Bryce Leead5b8322018-03-08 14:28:52 -0800377 }
378
379 /**
380 * Ensures that values specified at launch time are passed to {@link LaunchParamsModifier}
381 * when we are laying out a new task.
382 */
383 @Test
384 public void testCreateTaskLayout() {
385 // modifier for validating passed values.
386 final LaunchParamsModifier modifier = mock(LaunchParamsModifier.class);
387 mService.mStackSupervisor.getLaunchParamsController().registerModifier(modifier);
388
389 // add custom values to activity info to make unique.
390 final ActivityInfo info = new ActivityInfo();
391 final Rect launchBounds = new Rect(0, 0, 20, 30);
Bryce Leead5b8322018-03-08 14:28:52 -0800392
393 final WindowLayout windowLayout =
394 new WindowLayout(10, .5f, 20, 1.0f, Gravity.NO_GRAVITY, 1, 1);
395
396 info.windowLayout = windowLayout;
397 info.applicationInfo = new ApplicationInfo();
398 info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
399
400 // create starter.
Bryce Lee32e09ef2018-03-19 15:29:49 -0700401 final ActivityStarter optionStarter = prepareStarter(0 /* launchFlags */);
Bryce Leead5b8322018-03-08 14:28:52 -0800402
403 final ActivityOptions options = ActivityOptions.makeBasic();
404 options.setLaunchBounds(launchBounds);
405
406 // run starter.
407 optionStarter
Bryce Leead5b8322018-03-08 14:28:52 -0800408 .setReason("testCreateTaskLayout")
409 .setActivityInfo(info)
410 .setActivityOptions(new SafeActivityOptions(options))
411 .execute();
412
Garfield Tan706dbcb2018-10-15 11:33:02 -0700413 // verify that values are passed to the modifier. Values are passed twice -- once for
414 // setting initial state, another when task is created.
415 verify(modifier, times(2)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
Bryce Leead5b8322018-03-08 14:28:52 -0800416 any(), any());
417 }
Bryce Lee32e09ef2018-03-19 15:29:49 -0700418
419 /**
420 * This test ensures that if the intent is being delivered to a
421 */
422 @Test
423 public void testSplitScreenDeliverToTop() {
424 final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
425
426 final ActivityRecord focusActivity = new ActivityBuilder(mService)
427 .setCreateTask(true)
428 .build();
429
430 focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
431
432 final ActivityRecord reusableActivity = new ActivityBuilder(mService)
433 .setCreateTask(true)
434 .build();
435
436 // Create reusable activity after entering split-screen so that it is the top secondary
437 // stack.
438 reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
439
440 // Set focus back to primary.
Andrii Kulian6a6c4f12018-07-16 21:23:33 -0700441 final ActivityStack focusStack = focusActivity.getStack();
442 focusStack.moveToFront("testSplitScreenDeliverToTop");
Bryce Lee32e09ef2018-03-19 15:29:49 -0700443
444 doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
445
446 final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
447
448 // Ensure result is delivering intent to top.
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900449 assertEquals(START_DELIVERED_TO_TOP, result);
Bryce Lee32e09ef2018-03-19 15:29:49 -0700450 }
451
452 /**
453 * This test ensures that if the intent is being delivered to a split-screen unfocused task
454 * reports it is brought to front instead of delivering to top.
455 */
456 @Test
457 public void testSplitScreenTaskToFront() {
458 final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
459
460 // Create reusable activity here first. Setting the windowing mode of the primary stack
461 // will move the existing standard full screen stack to secondary, putting this one on the
462 // bottom.
463 final ActivityRecord reusableActivity = new ActivityBuilder(mService)
464 .setCreateTask(true)
465 .build();
466
467 reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
468
469 final ActivityRecord focusActivity = new ActivityBuilder(mService)
470 .setCreateTask(true)
471 .build();
472
473 // Enter split-screen. Primary stack should have focus.
474 focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
475
476 doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
477
478 final int result = starter.setReason("testSplitScreenMoveToFront").execute();
479
480 // Ensure result is moving task to front.
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900481 assertEquals(START_TASK_TO_FRONT, result);
Bryce Lee32e09ef2018-03-19 15:29:49 -0700482 }
Bryce Lee2b8e0372018-04-05 17:01:37 -0700483
484 /**
485 * Tests activity is cleaned up properly in a task mode violation.
486 */
487 @Test
488 public void testTaskModeViolation() {
489 final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
Andrii Kulian6a6c4f12018-07-16 21:23:33 -0700490 ((TestActivityDisplay) display).removeAllTasks();
Bryce Lee2b8e0372018-04-05 17:01:37 -0700491 assertNoTasks(display);
492
493 final ActivityStarter starter = prepareStarter(0);
494
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700495 final LockTaskController lockTaskController = mService.getLockTaskController();
Bryce Lee2b8e0372018-04-05 17:01:37 -0700496 doReturn(true).when(lockTaskController).isLockTaskModeViolation(any());
497
498 final int result = starter.setReason("testTaskModeViolation").execute();
499
500 assertEquals(START_RETURN_LOCK_TASK_MODE_VIOLATION, result);
501 assertNoTasks(display);
502 }
503
504 private void assertNoTasks(ActivityDisplay display) {
505 for (int i = display.getChildCount() - 1; i >= 0; --i) {
506 final ActivityStack stack = display.getChildAt(i);
Tadashi G. Takaoka74ccec22018-10-23 11:07:13 +0900507 assertThat(stack.getAllTasks()).isEmpty();
Bryce Lee2b8e0372018-04-05 17:01:37 -0700508 }
509 }
Michal Karpinski201bc0c2018-07-20 15:32:00 +0100510
511 /**
512 * This test ensures that activity starts are not being logged when the logging is disabled.
513 */
514 @Test
515 public void testActivityStartsLogging_noLoggingWhenDisabled() {
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700516 doReturn(false).when(mService).isActivityStartsLoggingEnabled();
Michal Karpinski201bc0c2018-07-20 15:32:00 +0100517 doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
518
519 ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK);
520 starter.setReason("testActivityStartsLogging_noLoggingWhenDisabled").execute();
521
522 // verify logging wasn't done
523 verify(mActivityMetricsLogger, never()).logActivityStart(any(), any(), any(), anyInt(),
524 any(), anyInt(), anyBoolean(), anyInt(), anyInt(), anyBoolean(), anyInt(), any(),
525 anyInt(), anyBoolean(), any(), anyBoolean());
526 }
527
528 /**
529 * This test ensures that activity starts are being logged when the logging is enabled.
530 */
531 @Test
532 public void testActivityStartsLogging_logsWhenEnabled() {
533 // note: conveniently this package doesn't have any activity visible
Wale Ogunwale342fbe92018-10-09 08:44:10 -0700534 doReturn(true).when(mService).isActivityStartsLoggingEnabled();
Michal Karpinski201bc0c2018-07-20 15:32:00 +0100535 doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
536
537 ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
538 .setCallingUid(FAKE_CALLING_UID)
539 .setRealCallingUid(FAKE_REAL_CALLING_UID)
540 .setCallingPackage(FAKE_CALLING_PACKAGE)
541 .setOriginatingPendingIntent(null);
542
543 starter.setReason("testActivityStartsLogging_logsWhenEnabled").execute();
544
545 // verify the above activity start was logged
546 verify(mActivityMetricsLogger, times(1)).logActivityStart(any(), any(), any(),
547 eq(FAKE_CALLING_UID), eq(FAKE_CALLING_PACKAGE), anyInt(), anyBoolean(),
548 eq(FAKE_REAL_CALLING_UID), anyInt(), anyBoolean(), anyInt(),
549 eq(ActivityBuilder.getDefaultComponent().getPackageName()), anyInt(), anyBoolean(),
550 any(), eq(false));
551 }
Riddle Hsub70b36d2018-09-11 21:20:02 +0800552
553 /**
554 * This test ensures that when starting an existing single task activity on secondary display
555 * which is not the top focused display, it should deliver new intent to the activity and not
556 * create a new stack.
557 */
558 @Test
559 public void testDeliverIntentToTopActivityOfNonTopDisplay() {
560 final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
561 false /* mockGetLaunchStack */);
562
563 // Create a secondary display at bottom.
Riddle Hsufd4a0502018-10-16 01:05:16 +0800564 final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
565 mSupervisor.addChild(secondaryDisplay, POSITION_BOTTOM);
Riddle Hsub70b36d2018-09-11 21:20:02 +0800566 final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
567 ACTIVITY_TYPE_STANDARD, true /* onTop */);
568
569 // Create an activity record on the top of secondary display.
Riddle Hsufd4a0502018-10-16 01:05:16 +0800570 final ActivityRecord topActivityOnSecondaryDisplay = createSingleTaskActivityOn(stack);
Riddle Hsub70b36d2018-09-11 21:20:02 +0800571
572 // Put an activity on default display as the top focused activity.
573 new ActivityBuilder(mService).setCreateTask(true).build();
574
575 // Start activity with the same intent as {@code topActivityOnSecondaryDisplay}
576 // on secondary display.
577 final ActivityOptions options = ActivityOptions.makeBasic()
578 .setLaunchDisplayId(secondaryDisplay.mDisplayId);
579 final int result = starter.setReason("testDeliverIntentToTopActivityOfNonTopDisplay")
580 .setIntent(topActivityOnSecondaryDisplay.intent)
581 .setActivityOptions(options.toBundle())
582 .execute();
583
584 // Ensure result is delivering intent to top.
585 assertEquals(START_DELIVERED_TO_TOP, result);
586
587 // Ensure secondary display only creates one stack.
588 verify(secondaryDisplay, times(1)).createStack(anyInt(), anyInt(), anyBoolean());
589 }
590
591 /**
Riddle Hsufd4a0502018-10-16 01:05:16 +0800592 * This test ensures that when starting an existing non-top single task activity on secondary
593 * display which is the top focused display, it should bring the task to front without creating
594 * unused stack.
595 */
596 @Test
597 public void testBringTaskToFrontOnSecondaryDisplay() {
598 final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
599 false /* mockGetLaunchStack */);
600
601 // Create a secondary display with an activity.
602 final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
603 mSupervisor.addChild(secondaryDisplay, POSITION_TOP);
604 final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
605 secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
606 ACTIVITY_TYPE_STANDARD, false /* onTop */));
607
608 // Create another activity on top of the secondary display.
609 final ActivityStack topStack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
610 ACTIVITY_TYPE_STANDARD, true /* onTop */);
611 final TaskRecord topTask = new TaskBuilder(mSupervisor).setStack(topStack).build();
612 new ActivityBuilder(mService).setTask(topTask).build();
613
614 // Start activity with the same intent as {@code singleTaskActivity} on secondary display.
615 final ActivityOptions options = ActivityOptions.makeBasic()
616 .setLaunchDisplayId(secondaryDisplay.mDisplayId);
617 final int result = starter.setReason("testBringTaskToFrontOnSecondaryDisplay")
618 .setIntent(singleTaskActivity.intent)
619 .setActivityOptions(options.toBundle())
620 .execute();
621
622 // Ensure result is moving existing task to front.
623 assertEquals(START_TASK_TO_FRONT, result);
624
625 // Ensure secondary display only creates two stacks.
626 verify(secondaryDisplay, times(2)).createStack(anyInt(), anyInt(), anyBoolean());
627 }
628
629 private ActivityRecord createSingleTaskActivityOn(ActivityStack stack) {
630 final ComponentName componentName = ComponentName.createRelative(
631 DEFAULT_COMPONENT_PACKAGE_NAME,
632 DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity");
633 final TaskRecord taskRecord = new TaskBuilder(mSupervisor)
634 .setComponent(componentName)
635 .setStack(stack)
636 .build();
637 return new ActivityBuilder(mService)
638 .setComponent(componentName)
639 .setLaunchMode(LAUNCH_SINGLE_TASK)
640 .setTask(taskRecord)
641 .build();
642 }
643
644 /**
Riddle Hsub70b36d2018-09-11 21:20:02 +0800645 * This test ensures that a reused top activity in the top focused stack is able to be
646 * reparented to another display.
647 */
648 @Test
649 public void testReparentTopFocusedActivityToSecondaryDisplay() {
650 final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
651 false /* mockGetLaunchStack */);
652
653 // Create a secondary display at bottom.
654 final TestActivityDisplay secondaryDisplay = addNewActivityDisplayAt(POSITION_BOTTOM);
655 secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
656 true /* onTop */);
657
658 // Put an activity on default display as the top focused activity.
659 final ActivityRecord topActivity = new ActivityBuilder(mService)
660 .setCreateTask(true)
661 .setLaunchMode(LAUNCH_SINGLE_TASK)
662 .build();
663
664 // Start activity with the same intent as {@code topActivity} on secondary display.
665 final ActivityOptions options = ActivityOptions.makeBasic()
666 .setLaunchDisplayId(secondaryDisplay.mDisplayId);
667 starter.setReason("testReparentTopFocusedActivityToSecondaryDisplay")
668 .setIntent(topActivity.intent)
669 .setActivityOptions(options.toBundle())
670 .execute();
671
672 // Ensure the activity is moved to secondary display.
673 assertEquals(secondaryDisplay, topActivity.getDisplay());
674 }
Wale Ogunwale44f036f2017-09-29 05:09:09 -0700675}