1/*
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
19import static android.app.ActivityManager.START_ABORTED;
20import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
21import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
22import static android.app.ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
23import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
24import static android.app.ActivityManager.START_NOT_VOICE_COMPATIBLE;
25import static android.app.ActivityManager.START_PERMISSION_DENIED;
26import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
27import static android.app.ActivityManager.START_SUCCESS;
28import static android.app.ActivityManager.START_SWITCHES_CANCELED;
29import static android.app.ActivityManager.START_TASK_TO_FRONT;
30import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
31import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
32import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
33import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
34import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
35
36import android.app.ActivityOptions;
37import android.app.IApplicationThread;
38import android.content.ComponentName;
39import android.content.Intent;
40import android.content.pm.ActivityInfo;
41import android.content.pm.ActivityInfo.WindowLayout;
42import android.content.pm.ApplicationInfo;
43import android.content.pm.IPackageManager;
44import android.graphics.Rect;
45import android.os.IBinder;
46import android.os.RemoteException;
47import android.platform.test.annotations.Presubmit;
48import android.service.voice.IVoiceInteractionSession;
49import android.support.test.filters.SmallTest;
50import android.support.test.runner.AndroidJUnit4;
51import android.view.Gravity;
52
53import org.junit.runner.RunWith;
54import org.junit.Test;
55
56import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
57import static com.android.server.am.ActivityManagerService.ANIMATE;
58
59import static org.junit.Assert.assertEquals;
60import static org.junit.Assert.assertTrue;
61import static org.mockito.Mockito.any;
62import static org.mockito.Mockito.anyBoolean;
63import static org.mockito.Mockito.anyInt;
64import static org.mockito.Mockito.anyObject;
65import static org.mockito.Mockito.doAnswer;
66import static org.mockito.Mockito.doNothing;
67import static org.mockito.Mockito.doReturn;
68import static org.mockito.Mockito.eq;
69import static org.mockito.Mockito.mock;
70import static org.mockito.Mockito.spy;
71import static org.mockito.Mockito.verify;
72import static org.mockito.Mockito.times;
73
74import com.android.internal.os.BatteryStatsImpl;
75import com.android.server.am.ActivityStarter.Factory;
76import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
77import com.android.server.am.TaskRecord.TaskRecordFactory;
78
79import java.util.ArrayList;
80
81/**
82 * Tests for the {@link ActivityStarter} class.
83 *
84 * Build/Install/Run:
85 *  atest FrameworksServicesTests:ActivityStarterTests
86 */
87@SmallTest
88@Presubmit
89@RunWith(AndroidJUnit4.class)
90public class ActivityStarterTests extends ActivityTestsBase {
91    private ActivityManagerService mService;
92    private ActivityStarter mStarter;
93    private ActivityStartController mController;
94
95    private static final int PRECONDITION_NO_CALLER_APP = 1;
96    private static final int PRECONDITION_NO_INTENT_COMPONENT = 1 << 1;
97    private static final int PRECONDITION_NO_ACTIVITY_INFO = 1 << 2;
98    private static final int PRECONDITION_SOURCE_PRESENT = 1 << 3;
99    private static final int PRECONDITION_REQUEST_CODE = 1 << 4;
100    private static final int PRECONDITION_SOURCE_VOICE_SESSION = 1 << 5;
101    private static final int PRECONDITION_NO_VOICE_SESSION_SUPPORT = 1 << 6;
102    private static final int PRECONDITION_DIFFERENT_UID = 1 << 7;
103    private static final int PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION = 1 << 8;
104    private static final int PRECONDITION_CANNOT_START_ANY_ACTIVITY = 1 << 9;
105    private static final int PRECONDITION_DISALLOW_APP_SWITCHING = 1 << 10;
106
107    @Override
108    public void setUp() throws Exception {
109        super.setUp();
110        mService = createActivityManagerService();
111        mController = mock(ActivityStartController.class);
112        mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor,
113                mock(ActivityStartInterceptor.class));
114    }
115
116    @Test
117    public void testUpdateLaunchBounds() throws Exception {
118        // When in a non-resizeable stack, the task bounds should be updated.
119        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
120                .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
121                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
122                .build();
123        final Rect bounds = new Rect(10, 10, 100, 100);
124
125        mStarter.updateBounds(task, bounds);
126        assertEquals(task.getOverrideBounds(), bounds);
127        assertEquals(new Rect(), task.getStack().getOverrideBounds());
128
129        // When in a resizeable stack, the stack bounds should be updated as well.
130        final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
131                .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
132                        WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
133                .build();
134        assertTrue(task2.getStack() instanceof PinnedActivityStack);
135        mStarter.updateBounds(task2, bounds);
136
137        verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
138                eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
139
140        // In the case of no animation, the stack and task bounds should be set immediately.
141        if (!ANIMATE) {
142            assertEquals(task2.getStack().getOverrideBounds(), bounds);
143            assertEquals(task2.getOverrideBounds(), bounds);
144        } else {
145            assertEquals(task2.getOverrideBounds(), new Rect());
146        }
147    }
148
149    @Test
150    public void testStartActivityPreconditions() throws Exception {
151        verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED);
152        verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT,
153                START_INTENT_NOT_RESOLVED);
154        verifyStartActivityPreconditions(PRECONDITION_NO_ACTIVITY_INFO, START_CLASS_NOT_FOUND);
155        verifyStartActivityPreconditions(PRECONDITION_SOURCE_PRESENT | PRECONDITION_REQUEST_CODE,
156                Intent.FLAG_ACTIVITY_FORWARD_RESULT, START_FORWARD_AND_REQUEST_CONFLICT);
157        verifyStartActivityPreconditions(
158                PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
159                        | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID,
160                START_NOT_VOICE_COMPATIBLE);
161        verifyStartActivityPreconditions(
162                PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
163                        | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID
164                        | PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION,
165                START_NOT_VOICE_COMPATIBLE);
166        verifyStartActivityPreconditions(PRECONDITION_CANNOT_START_ANY_ACTIVITY, START_ABORTED);
167        verifyStartActivityPreconditions(PRECONDITION_DISALLOW_APP_SWITCHING,
168                START_SWITCHES_CANCELED);
169    }
170
171    private static boolean containsConditions(int preconditions, int mask) {
172        return (preconditions & mask) == mask;
173    }
174
175    private void verifyStartActivityPreconditions(int preconditions, int expectedResult) {
176        verifyStartActivityPreconditions(preconditions, 0 /*launchFlags*/, expectedResult);
177    }
178
179    /**
180     * Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
181     * provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
182     * and the launch flags specified in the intent. The method constructs a call to
183     * {@link ActivityStarter#execute} based on these preconditions and ensures the result matches
184     * the expected. It is important to note that the method also checks side effects of the start,
185     * such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios.
186     * @param preconditions A bitmask representing the preconditions for the launch
187     * @param launchFlags The launch flags to be provided by the launch {@link Intent}.
188     * @param expectedResult The expected result from the launch.
189     */
190    private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
191            int expectedResult) {
192        final ActivityManagerService service = createActivityManagerService();
193        final IPackageManager packageManager = mock(IPackageManager.class);
194        final ActivityStartController controller = mock(ActivityStartController.class);
195
196        final ActivityStarter starter = new ActivityStarter(controller, service,
197                service.mStackSupervisor, mock(ActivityStartInterceptor.class));
198        final IApplicationThread caller = mock(IApplicationThread.class);
199
200        // If no caller app, return {@code null} {@link ProcessRecord}.
201        final ProcessRecord record = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
202                ? null : new ProcessRecord(null, mock(BatteryStatsImpl.class),
203                mock(ApplicationInfo.class), null, 0);
204
205        doReturn(record).when(service).getRecordForAppLocked(anyObject());
206
207        final Intent intent = new Intent();
208        intent.setFlags(launchFlags);
209
210        final ActivityInfo aInfo = containsConditions(preconditions, PRECONDITION_NO_ACTIVITY_INFO)
211                ?  null : new ActivityInfo();
212
213        IVoiceInteractionSession voiceSession =
214                containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
215                ? mock(IVoiceInteractionSession.class) : null;
216
217        // Create source token
218        final ActivityBuilder builder = new ActivityBuilder(service).setTask(
219                new TaskBuilder(service.mStackSupervisor).setVoiceSession(voiceSession).build());
220
221        if (aInfo != null) {
222            aInfo.applicationInfo = new ApplicationInfo();
223            aInfo.applicationInfo.packageName =
224                    ActivityBuilder.getDefaultComponent().getPackageName();
225        }
226
227        // Offset uid by one from {@link ActivityInfo} to simulate different uids.
228        if (containsConditions(preconditions, PRECONDITION_DIFFERENT_UID)) {
229            builder.setUid(aInfo.applicationInfo.uid + 1);
230        }
231
232        final ActivityRecord source = builder.build();
233
234        if (!containsConditions(preconditions, PRECONDITION_NO_INTENT_COMPONENT)) {
235            intent.setComponent(source.realActivity);
236        }
237
238        if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
239            doReturn(false).when(service).checkAppSwitchAllowedLocked(anyInt(), anyInt(), anyInt(),
240                    anyInt(), any());
241        }
242
243        if (containsConditions(preconditions,PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
244            doReturn(false).when(service.mStackSupervisor).checkStartAnyActivityPermission(
245                    any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
246                    anyBoolean(), anyBoolean(), any(), any(), any());
247        }
248
249        try {
250            if (containsConditions(preconditions,
251                    PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION)) {
252                doAnswer((inv) -> {
253                    throw new RemoteException();
254                }).when(packageManager).activitySupportsIntent(eq(source.realActivity), eq(intent),
255                        any());
256            } else {
257                doReturn(!containsConditions(preconditions, PRECONDITION_NO_VOICE_SESSION_SUPPORT))
258                        .when(packageManager).activitySupportsIntent(eq(source.realActivity),
259                        eq(intent), any());
260            }
261        } catch (RemoteException e) {
262        }
263
264        final IBinder resultTo = containsConditions(preconditions, PRECONDITION_SOURCE_PRESENT)
265                || containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
266                ? source.appToken : null;
267
268        final int requestCode = containsConditions(preconditions, PRECONDITION_REQUEST_CODE)
269                ? 1 : 0;
270
271        final int result = starter.setCaller(caller)
272                .setIntent(intent)
273                .setActivityInfo(aInfo)
274                .setResultTo(resultTo)
275                .setRequestCode(requestCode)
276                .setReason("testLaunchActivityPermissionDenied")
277                .execute();
278
279        // In some cases the expected result internally is different than the published result. We
280        // must use ActivityStarter#getExternalResult to translate.
281        assertEquals(ActivityStarter.getExternalResult(expectedResult), result);
282
283        // Ensure that {@link ActivityOptions} are aborted with unsuccessful result.
284        if (expectedResult != START_SUCCESS) {
285            final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
286                    mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
287            final ActivityOptions options = spy(ActivityOptions.makeBasic());
288
289            final int optionResult = optionStarter.setCaller(caller)
290                    .setIntent(intent)
291                    .setActivityInfo(aInfo)
292                    .setResultTo(resultTo)
293                    .setRequestCode(requestCode)
294                    .setReason("testLaunchActivityPermissionDenied")
295                    .setActivityOptions(new SafeActivityOptions(options))
296                    .execute();
297            verify(options, times(1)).abort();
298        }
299    }
300
301    private ActivityStarter prepareStarter(int launchFlags) {
302        // always allow test to start activity.
303        doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
304                any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
305                anyBoolean(), anyBoolean(), any(), any(), any());
306
307        // instrument the stack and task used.
308        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
309                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
310        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
311                .setCreateStack(false)
312                .build();
313
314        // supervisor needs a focused stack.
315        mService.mStackSupervisor.mFocusedStack = stack;
316
317        // use factory that only returns spy task.
318        final TaskRecordFactory factory = mock(TaskRecordFactory.class);
319        TaskRecord.setTaskRecordFactory(factory);
320
321        // return task when created.
322        doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any());
323
324        // direct starter to use spy stack.
325        doReturn(stack).when(mService.mStackSupervisor)
326                .getLaunchStack(any(), any(), any(), anyBoolean());
327        doReturn(stack).when(mService.mStackSupervisor)
328                .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
329
330        final Intent intent = new Intent();
331        intent.addFlags(launchFlags);
332        intent.setComponent(ActivityBuilder.getDefaultComponent());
333
334        final ActivityInfo info = new ActivityInfo();
335
336        info.applicationInfo = new ApplicationInfo();
337        info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
338
339        return new ActivityStarter(mController, mService,
340                mService.mStackSupervisor, mock(ActivityStartInterceptor.class))
341                .setIntent(intent)
342                .setActivityInfo(info);
343    }
344
345    /**
346     * Ensures that values specified at launch time are passed to {@link LaunchParamsModifier}
347     * when we are laying out a new task.
348     */
349    @Test
350    public void testCreateTaskLayout() {
351        // modifier for validating passed values.
352        final LaunchParamsModifier modifier = mock(LaunchParamsModifier.class);
353        mService.mStackSupervisor.getLaunchParamsController().registerModifier(modifier);
354
355        // add custom values to activity info to make unique.
356        final ActivityInfo info = new ActivityInfo();
357        final Rect launchBounds = new Rect(0, 0, 20, 30);
358
359        final WindowLayout windowLayout =
360                new WindowLayout(10, .5f, 20, 1.0f, Gravity.NO_GRAVITY, 1, 1);
361
362        info.windowLayout = windowLayout;
363        info.applicationInfo = new ApplicationInfo();
364        info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
365
366        // create starter.
367        final ActivityStarter optionStarter = prepareStarter(0 /* launchFlags */);
368
369        final ActivityOptions options = ActivityOptions.makeBasic();
370        options.setLaunchBounds(launchBounds);
371
372        // run starter.
373        optionStarter
374                .setReason("testCreateTaskLayout")
375                .setActivityInfo(info)
376                .setActivityOptions(new SafeActivityOptions(options))
377                .execute();
378
379        // verify that values are passed to the modifier.
380        verify(modifier, times(1)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
381                any(), any());
382    }
383
384    /**
385     * This test ensures that if the intent is being delivered to a
386     */
387    @Test
388    public void testSplitScreenDeliverToTop() {
389        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
390
391        final ActivityRecord focusActivity = new ActivityBuilder(mService)
392                .setCreateTask(true)
393                .build();
394
395        focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
396
397        final ActivityRecord reusableActivity = new ActivityBuilder(mService)
398                .setCreateTask(true)
399                .build();
400
401        // Create reusable activity after entering split-screen so that it is the top secondary
402        // stack.
403        reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
404
405        // Set focus back to primary.
406        mService.mStackSupervisor.setFocusStackUnchecked("testSplitScreenDeliverToTop",
407                focusActivity.getStack());
408
409        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
410
411        final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
412
413        // Ensure result is delivering intent to top.
414        assertEquals(result, START_DELIVERED_TO_TOP);
415    }
416
417    /**
418     * This test ensures that if the intent is being delivered to a split-screen unfocused task
419     * reports it is brought to front instead of delivering to top.
420     */
421    @Test
422    public void testSplitScreenTaskToFront() {
423        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
424
425        // Create reusable activity here first. Setting the windowing mode of the primary stack
426        // will move the existing standard full screen stack to secondary, putting this one on the
427        // bottom.
428        final ActivityRecord reusableActivity = new ActivityBuilder(mService)
429                .setCreateTask(true)
430                .build();
431
432        reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
433
434        final ActivityRecord focusActivity = new ActivityBuilder(mService)
435                .setCreateTask(true)
436                .build();
437
438        // Enter split-screen. Primary stack should have focus.
439        focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
440
441        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
442
443        final int result = starter.setReason("testSplitScreenMoveToFront").execute();
444
445        // Ensure result is moving task to front.
446        assertEquals(result, START_TASK_TO_FRONT);
447    }
448
449    /**
450     * Tests activity is cleaned up properly in a task mode violation.
451     */
452    @Test
453    public void testTaskModeViolation() {
454        final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
455        assertNoTasks(display);
456
457        final ActivityStarter starter = prepareStarter(0);
458
459        final LockTaskController lockTaskController = mService.getLockTaskController();
460        doReturn(true).when(lockTaskController).isLockTaskModeViolation(any());
461
462        final int result = starter.setReason("testTaskModeViolation").execute();
463
464        assertEquals(START_RETURN_LOCK_TASK_MODE_VIOLATION, result);
465        assertNoTasks(display);
466    }
467
468    private void assertNoTasks(ActivityDisplay display) {
469        for (int i = display.getChildCount() - 1; i >= 0; --i) {
470            final ActivityStack stack = display.getChildAt(i);
471            assertTrue(stack.getAllTasks().isEmpty());
472        }
473    }
474}
475