• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 
17 package android.server.wm;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
24 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
25 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
26 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
27 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
28 import static android.server.wm.WindowManagerState.STATE_INITIALIZING;
29 import static android.server.wm.WindowManagerState.STATE_STOPPED;
30 import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
31 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
32 import static android.server.wm.app.Components.NO_RELAUNCH_ACTIVITY;
33 import static android.server.wm.app.Components.TEST_ACTIVITY;
34 import static android.server.wm.app.Components.TRANSLUCENT_ACTIVITY;
35 import static android.server.wm.app.Components.TestActivity.COMMAND_NAVIGATE_UP_TO;
36 import static android.server.wm.app.Components.TestActivity.COMMAND_START_ACTIVITIES;
37 import static android.server.wm.app.Components.TestActivity.EXTRA_INTENT;
38 import static android.server.wm.app.Components.TestActivity.EXTRA_INTENTS;
39 import static android.server.wm.app27.Components.SDK_27_LAUNCHING_ACTIVITY;
40 import static android.server.wm.second.Components.SECOND_ACTIVITY;
41 import static android.view.Display.DEFAULT_DISPLAY;
42 
43 import static com.google.common.truth.Truth.assertWithMessage;
44 
45 import static org.junit.Assert.assertEquals;
46 import static org.junit.Assert.assertFalse;
47 import static org.junit.Assert.assertNotEquals;
48 
49 import android.app.Activity;
50 import android.app.ActivityOptions;
51 import android.content.ComponentName;
52 import android.content.Context;
53 import android.content.Intent;
54 import android.content.pm.PackageManager;
55 import android.os.Bundle;
56 import android.platform.test.annotations.Presubmit;
57 import android.server.wm.CommandSession.ActivitySession;
58 import android.server.wm.intent.Activities;
59 
60 import com.android.compatibility.common.util.ApiTest;
61 
62 import org.junit.Test;
63 
64 import java.util.Arrays;
65 import java.util.List;
66 import java.util.stream.Collectors;
67 import java.util.stream.Stream;
68 
69 /**
70  * Build/Install/Run:
71  *     atest CtsWindowManagerDeviceTestCases:StartActivityTests
72  */
73 @Presubmit
74 public class StartActivityTests extends ActivityManagerTestBase {
75 
76     @Test
testStartHomeIfNoActivities()77     public void testStartHomeIfNoActivities() {
78         if (!hasHomeScreen()) {
79 	    return;
80 	}
81 
82         final ComponentName defaultHome = getDefaultHomeComponent();
83         final int[] allActivityTypes = Arrays.copyOf(ALL_ACTIVITY_TYPE_BUT_HOME,
84                 ALL_ACTIVITY_TYPE_BUT_HOME.length + 1);
85         allActivityTypes[allActivityTypes.length - 1] = ACTIVITY_TYPE_HOME;
86         removeRootTasksWithActivityTypes(allActivityTypes);
87 
88         waitAndAssertResumedActivity(defaultHome,
89                 "Home activity should be restarted after force-finish");
90 
91         stopTestPackage(defaultHome.getPackageName());
92 
93         waitAndAssertResumedActivity(defaultHome,
94                 "Home activity should be restarted after force-stop");
95     }
96 
97     /**
98      * Ensures {@link Activity} without {@link Intent#FLAG_ACTIVITY_NEW_TASK} can only be launched
99      * from an {@link Activity} {@link android.content.Context}.
100      */
101     @Test
testStartActivityContexts()102     public void testStartActivityContexts() {
103         // Note by default LaunchActivityBuilder will use LAUNCHING_ACTIVITY to launch the target.
104 
105         // Launch Activity from application context without FLAG_ACTIVITY_NEW_TASK.
106         getLaunchActivityBuilder()
107                 .setTargetActivity(TEST_ACTIVITY)
108                 .setUseApplicationContext(true)
109                 .setSuppressExceptions(true)
110                 .setWaitForLaunched(false)
111                 .execute();
112 
113         // Launch another activity from activity to ensure previous one has done.
114         getLaunchActivityBuilder()
115                 .setTargetActivity(NO_RELAUNCH_ACTIVITY)
116                 .execute();
117 
118         mWmState.computeState(NO_RELAUNCH_ACTIVITY);
119 
120         // Verify Activity was not started.
121         assertFalse(mWmState.containsActivity(TEST_ACTIVITY));
122         mWmState.assertResumedActivity(
123                 "Activity launched from activity context should be present", NO_RELAUNCH_ACTIVITY);
124     }
125 
126     /**
127      * Ensures you can start an {@link Activity} from a non {@link Activity}
128      * {@link android.content.Context} with the {@code FLAG_ACTIVITY_NEW_TASK}.
129      */
130     @Test
testStartActivityNewTask()131     public void testStartActivityNewTask() throws Exception {
132         // Launch Activity from application context.
133         getLaunchActivityBuilder()
134                 .setTargetActivity(TEST_ACTIVITY)
135                 .setUseApplicationContext(true)
136                 .setSuppressExceptions(true)
137                 .setNewTask(true)
138                 .execute();
139 
140         mWmState.computeState(TEST_ACTIVITY);
141         mWmState.assertResumedActivity("Test Activity should be started with new task flag",
142                 TEST_ACTIVITY);
143     }
144 
145     @Test
testStartActivityTaskLaunchBehind()146     public void testStartActivityTaskLaunchBehind() {
147         // launch an activity
148         getLaunchActivityBuilder()
149                 .setTargetActivity(TEST_ACTIVITY)
150                 .setUseInstrumentation()
151                 .setNewTask(true)
152                 .execute();
153 
154         // launch an activity behind
155         getLaunchActivityBuilder()
156                 .setTargetActivity(TRANSLUCENT_ACTIVITY)
157                 .setUseInstrumentation()
158                 .setIntentFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
159                 .setNewTask(true)
160                 .setLaunchTaskBehind(true)
161                 .execute();
162 
163         waitAndAssertActivityState(TRANSLUCENT_ACTIVITY, STATE_STOPPED,
164                 "Activity should be stopped");
165         mWmState.assertResumedActivity("Test Activity should be remained on top and resumed",
166                 TEST_ACTIVITY);
167     }
168 
169     @Test
testStartActivityFromFinishingActivity()170     public void testStartActivityFromFinishingActivity() {
171         // launch TEST_ACTIVITY from LAUNCHING_ACTIVITY
172         getLaunchActivityBuilder()
173                 .setTargetActivity(TEST_ACTIVITY)
174                 .setFinishBeforeLaunch(true)
175                 .execute();
176 
177         // launch LAUNCHING_ACTIVITY again
178         getLaunchActivityBuilder()
179                 .setTargetActivity(LAUNCHING_ACTIVITY)
180                 .setUseInstrumentation()
181                 .setWaitForLaunched(false)
182                 .execute();
183 
184         // make sure TEST_ACTIVITY is still on top and resumed
185         mWmState.computeState(TEST_ACTIVITY);
186         mWmState.assertResumedActivity("Test Activity should be remained on top and resumed",
187                 TEST_ACTIVITY);
188     }
189 
190     /**
191      * Ensures you can start an {@link Activity} from a non {@link Activity}
192      * {@link android.content.Context} when the target sdk is between N and O Mr1.
193      * @throws Exception
194      */
195     @Test
testLegacyStartActivityFromNonActivityContext()196     public void testLegacyStartActivityFromNonActivityContext() {
197         getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
198                 .setLaunchingActivity(SDK_27_LAUNCHING_ACTIVITY)
199                 .setUseApplicationContext(true)
200                 .execute();
201 
202         mWmState.computeState(TEST_ACTIVITY);
203         mWmState.assertResumedActivity("Test Activity should be resumed without older sdk",
204                 TEST_ACTIVITY);
205     }
206 
207     /**
208      * Starts 3 activities A, B, C in the same task. A and B belong to current package and are not
209      * exported. C belongs to a different package with different uid. After C called
210      * {@link Activity#navigateUpTo(Intent)} with the intent of A, the activities B, C should be
211      * finished and instead of creating a new instance of A, the original A should become the top
212      * activity because the caller C in different uid cannot launch a non-exported activity.
213      */
214     @Test
215     @ApiTest(apis = {"android.app.Activity#navigateUpTo"})
testStartActivityByNavigateUpToFromDiffUid()216     public void testStartActivityByNavigateUpToFromDiffUid() {
217         final Intent rootIntent = new Intent(mContext, Activities.RegularActivity.class);
218         final String regularActivityName = Activities.RegularActivity.class.getName();
219         final TestActivitySession<Activities.RegularActivity> activitySession1 =
220                 createManagedTestActivitySession();
221         activitySession1.launchTestActivityOnDisplaySync(regularActivityName, rootIntent,
222                 DEFAULT_DISPLAY);
223 
224         final Intent navIntent = new Intent(mContext, Activities.RegularActivity.class);
225         verifyNavigateUpTo(activitySession1, navIntent);
226 
227         navIntent.addFlags(FLAG_ACTIVITY_CLEAR_TOP);
228         verifyNavigateUpTo(activitySession1, navIntent);
229         assertFalse("#onNewIntent cannot be called",
230                 activitySession1.getActivity().mIsOnNewIntentCalled);
231     }
232 
verifyNavigateUpTo(TestActivitySession rootActivitySession, Intent navIntent)233     private void verifyNavigateUpTo(TestActivitySession rootActivitySession, Intent navIntent) {
234         final TestActivitySession<Activities.SingleTopActivity> activitySession2 =
235                 createManagedTestActivitySession();
236         activitySession2.launchTestActivityOnDisplaySync(Activities.SingleTopActivity.class,
237                 DEFAULT_DISPLAY);
238 
239         final CommandSession.ActivitySession activitySession3 =
240                 createManagedActivityClientSession().startActivity(
241                         new CommandSession.DefaultLaunchProxy() {
242                             @Override
243                             public void execute() {
244                                 final Intent intent = new Intent().setComponent(TEST_ACTIVITY);
245                                 mLaunchInjector.setupIntent(intent);
246                                 activitySession2.getActivity().startActivity(intent);
247                             }
248                         });
249 
250         final Bundle data = new Bundle();
251         data.putParcelable(EXTRA_INTENT, navIntent);
252         activitySession3.sendCommand(COMMAND_NAVIGATE_UP_TO, data);
253 
254         waitAndAssertTopResumedActivity(rootActivitySession.getActivity().getComponentName(),
255                 DEFAULT_DISPLAY, "navigateUpTo should return to the first activity");
256         // Make sure the resumed first activity is the original instance.
257         assertFalse("The target of navigateUpTo should not be destroyed",
258                 rootActivitySession.getActivity().isDestroyed());
259 
260         // The activities above the first one should be destroyed.
261         mWmState.waitAndAssertActivityRemoved(
262                 activitySession3.getOriginalLaunchIntent().getComponent());
263         mWmState.waitAndAssertActivityRemoved(activitySession2.getActivity().getComponentName());
264     }
265 
266     /**
267      * Assume there are 3 activities (A1, A2, A3) with different task affinities and the same uid.
268      * After A1 called {@link Activity#startActivities} to start A2 (with NEW_TASK) and A3, the
269      * result should be 2 tasks: [A1] and [A2, A3].
270      */
271     @Test
testStartActivitiesInNewAndSameTask()272     public void testStartActivitiesInNewAndSameTask() {
273         final int[] taskIds = startActivitiesAndGetTaskIds(new Intent[] {
274                 new Intent().setComponent(NO_RELAUNCH_ACTIVITY)
275                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
276                 new Intent().setComponent(LAUNCHING_ACTIVITY) });
277 
278         assertNotEquals("The activity with different task affinity started by flag NEW_TASK"
279                 + " should be in a different task", taskIds[0], taskIds[1]);
280         assertEquals("The activity started without flag NEW_TASK should be put in the same task",
281                 taskIds[1], taskIds[2]);
282     }
283 
284     @Test
testNormalActivityCanNotSetActivityType()285     public void testNormalActivityCanNotSetActivityType() {
286         // Activities should not be started if the launch activity type is set.
287         boolean useShellPermission = false;
288         startingActivityWithType(ACTIVITY_TYPE_STANDARD, useShellPermission);
289         startingActivityWithType(ACTIVITY_TYPE_HOME, useShellPermission);
290         startingActivityWithType(ACTIVITY_TYPE_RECENTS, useShellPermission);
291         startingActivityWithType(ACTIVITY_TYPE_ASSISTANT, useShellPermission);
292         startingActivityWithType(ACTIVITY_TYPE_DREAM, useShellPermission);
293 
294         // Activities can be started because they are started with shell permissions.
295         useShellPermission = true;
296         startingActivityWithType(ACTIVITY_TYPE_STANDARD, useShellPermission);
297         startingActivityWithType(ACTIVITY_TYPE_HOME, useShellPermission);
298         startingActivityWithType(ACTIVITY_TYPE_RECENTS, useShellPermission);
299         startingActivityWithType(ACTIVITY_TYPE_ASSISTANT, useShellPermission);
300         startingActivityWithType(ACTIVITY_TYPE_DREAM, useShellPermission);
301     }
302 
startingActivityWithType(int type, boolean useShellPermission)303     private void startingActivityWithType(int type, boolean useShellPermission) {
304         separateTestJournal();
305         getLaunchActivityBuilder()
306                 .setTargetActivity(BROADCAST_RECEIVER_ACTIVITY)
307                 .setUseInstrumentation()
308                 .setWithShellPermission(useShellPermission)
309                 .setActivityType(type)
310                 .setWaitForLaunched(false)
311                 .setMultipleTask(true)
312                 .execute();
313 
314         mWmState.computeState();
315         if (useShellPermission) {
316             waitAndAssertResumedActivity(BROADCAST_RECEIVER_ACTIVITY,
317                     "Activity should be started and resumed");
318             if (type == ACTIVITY_TYPE_HOME && isAutomotive(mContext)
319                     && hasSplitscreenMultitaskingFeature(mContext)) {
320                 // For automotive devices with splitscreen multitasking, home activity might
321                 // not be in front of the stack, hence, check for its visibility instead.
322                 mWmState.assertHomeActivityVisible(/* visible= */ true);
323             } else {
324                 mWmState.assertFrontStackActivityType(
325                         "The activity type should be same as requested.", type);
326             }
327             mBroadcastActionTrigger.finishBroadcastReceiverActivity();
328             mWmState.waitAndAssertActivityRemoved(BROADCAST_RECEIVER_ACTIVITY);
329         } else {
330             assertSecurityExceptionFromActivityLauncher();
331         }
332     }
333 
334     /**
335      * Checks whether the device is automotive
336      */
isAutomotive(Context context)337     private static boolean isAutomotive(Context context) {
338         PackageManager pm = context.getPackageManager();
339         return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
340     }
341 
342     /**
343      * Checks whether the device has automotive splitscreen multitasking feature enabled
344      */
hasSplitscreenMultitaskingFeature(Context context)345     private static boolean hasSplitscreenMultitaskingFeature(Context context) {
346         PackageManager pm = context.getPackageManager();
347         return pm.hasSystemFeature(/* PackageManager.FEATURE_CAR_SPLITSCREEN_MULTITASKING */
348                 "android.software.car.splitscreen_multitasking");
349     }
350 
351     /**
352      * Assume there are 3 activities (A1, A2, B1) with default launch mode. The uid of B1 is
353      * different from A1 and A2. After A1 called {@link Activity#startActivities} to start B1 and
354      * A2, the result should be 3 tasks.
355      */
356     @Test
testStartActivitiesWithDiffUidNotInSameTask()357     public void testStartActivitiesWithDiffUidNotInSameTask() {
358         final int[] taskIds = startActivitiesAndGetTaskIds(new Intent[] {
359                 new Intent().setComponent(SECOND_ACTIVITY)
360                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
361                 new Intent().setComponent(LAUNCHING_ACTIVITY) });
362 
363         assertNotEquals("The activity in a different application (uid) started by flag NEW_TASK"
364                 + " should be in a different task", taskIds[0], taskIds[1]);
365         assertWithMessage("The last started activity should be in a different task because "
366                 + SECOND_ACTIVITY + " has a different uid from the source caller")
367                         .that(taskIds[2]).isNotIn(Arrays.asList(taskIds[0], taskIds[1]));
368     }
369 
370     /**
371      * Test the activity launched with ActivityOptions#setTaskOverlay should remain on top of the
372      * task after start another activity.
373      */
374     @Test
testStartActivitiesTaskOverlayStayOnTop()375     public void testStartActivitiesTaskOverlayStayOnTop() {
376         final Intent baseIntent = new Intent(mContext, Activities.RegularActivity.class);
377         final String regularActivityName = Activities.RegularActivity.class.getName();
378         final TestActivitySession<Activities.RegularActivity> activitySession =
379                 createManagedTestActivitySession();
380         activitySession.launchTestActivityOnDisplaySync(regularActivityName, baseIntent,
381                 DEFAULT_DISPLAY);
382         mWmState.computeState(baseIntent.getComponent());
383         final int taskId = mWmState.getTaskByActivity(baseIntent.getComponent()).getTaskId();
384         final Activity baseActivity = activitySession.getActivity();
385 
386         final ActivityOptions overlayOptions = ActivityOptions.makeBasic();
387         overlayOptions.setTaskOverlay(true, true);
388         overlayOptions.setLaunchTaskId(taskId);
389         final Intent taskOverlay = new Intent().setComponent(SECOND_ACTIVITY);
390         runWithShellPermission(() ->
391                 baseActivity.startActivity(taskOverlay, overlayOptions.toBundle()));
392 
393         waitAndAssertResumedActivity(taskOverlay.getComponent(),
394                 "taskOverlay activity on top");
395         final Intent behindOverlay = new Intent().setComponent(TEST_ACTIVITY);
396         baseActivity.startActivity(behindOverlay);
397 
398         waitAndAssertActivityState(TEST_ACTIVITY, STATE_INITIALIZING,
399                 "Activity behind taskOverlay should not resumed");
400         // check order: SecondActivity(top) -> TestActivity -> RegularActivity(base)
401         final List<String> activitiesOrder = mWmState.getTaskByActivity(baseIntent.getComponent())
402                 .mActivities
403                 .stream()
404                 .map(WindowManagerState.Activity::getName)
405                 .collect(Collectors.toList());
406 
407         final List<String> expectedOrder = Stream.of(
408                 SECOND_ACTIVITY,
409                 TEST_ACTIVITY,
410                 baseIntent.getComponent())
411                 .map(c -> c.flattenToShortString())
412                 .collect(Collectors.toList());
413         assertEquals(activitiesOrder, expectedOrder);
414         mWmState.assertResumedActivity("TaskOverlay activity should be remained on top and "
415                         + "resumed", taskOverlay.getComponent());
416     }
417 
418     /**
419      * Test the activity launched with ActivityOptions#setTaskOverlay should not be finished after
420      * launch another activity with clear_task flag.
421      */
422     @Test
testStartActivitiesTaskOverlayWithClearTask()423     public void testStartActivitiesTaskOverlayWithClearTask() {
424         verifyStartActivitiesTaskOverlayWithLaunchFlags(
425                 FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
426     }
427 
428     /**
429      * Test the activity launched with ActivityOptions#setTaskOverlay should not be finished after
430      * launch another activity with clear_top flag.
431      */
432     @Test
testStartActivitiesTaskOverlayWithClearTop()433     public void testStartActivitiesTaskOverlayWithClearTop() {
434         verifyStartActivitiesTaskOverlayWithLaunchFlags(FLAG_ACTIVITY_CLEAR_TOP);
435     }
436 
verifyStartActivitiesTaskOverlayWithLaunchFlags(int flags)437     private void verifyStartActivitiesTaskOverlayWithLaunchFlags(int flags) {
438         // Launch a regular activity
439         final Intent baseIntent = new Intent(mContext, Activities.RegularActivity.class);
440         final String regularActivityName = Activities.RegularActivity.class.getName();
441         final TestActivitySession<Activities.RegularActivity> activitySession =
442                 createManagedTestActivitySession();
443         activitySession.launchTestActivityOnDisplaySync(regularActivityName, baseIntent,
444                 DEFAULT_DISPLAY);
445         mWmState.computeState(baseIntent.getComponent());
446         final int taskId = mWmState.getTaskByActivity(baseIntent.getComponent()).getTaskId();
447         final Activity baseActivity = activitySession.getActivity();
448 
449         // Launch a taskOverlay activity
450         final ActivityOptions overlayOptions = ActivityOptions.makeBasic();
451         overlayOptions.setTaskOverlay(true, true);
452         overlayOptions.setLaunchTaskId(taskId);
453         final Intent taskOverlay = new Intent().setComponent(SECOND_ACTIVITY);
454         runWithShellPermission(() ->
455                 baseActivity.startActivity(taskOverlay, overlayOptions.toBundle()));
456         waitAndAssertResumedActivity(taskOverlay.getComponent(),
457                 "taskOverlay activity on top");
458 
459         // Launch the regular activity with specific flags
460         final Intent intent = new Intent(mContext, Activities.RegularActivity.class)
461                 .addFlags(flags);
462         baseActivity.startActivity(intent);
463 
464         waitAndAssertResumedActivity(taskOverlay.getComponent(),
465                 "taskOverlay activity on top");
466         assertEquals("Instance of the taskOverlay activity must exist", 1,
467                 mWmState.getActivityCountInTask(taskId, taskOverlay.getComponent()));
468         assertEquals("Activity must be in same task.", taskId,
469                 mWmState.getTaskByActivity(intent.getComponent()).getTaskId());
470     }
471 
472     /**
473      * Invokes {@link android.app.Activity#startActivities} from {@link #TEST_ACTIVITY} and returns
474      * the task id of each started activity (the index 0 will be the caller {@link #TEST_ACTIVITY}).
475      */
startActivitiesAndGetTaskIds(Intent[] intents)476     private int[] startActivitiesAndGetTaskIds(Intent[] intents) {
477         final ActivitySession activity = createManagedActivityClientSession()
478                 .startActivity(getLaunchActivityBuilder().setUseInstrumentation());
479         final Bundle intentBundle = new Bundle();
480         intentBundle.putParcelableArray(EXTRA_INTENTS, intents);
481         // The {@link Activity#startActivities} cannot be called from the instrumentation
482         // package because the implementation (given by test runner) may be overridden.
483         activity.sendCommand(COMMAND_START_ACTIVITIES, intentBundle);
484 
485         final int[] taskIds = new int[intents.length + 1];
486         // The {@code intents} are started, wait for the last (top) activity to be ready and then
487         // verify their task ids.
488         mWmState.computeState(intents[intents.length - 1].getComponent());
489         taskIds[0] = mWmState.getTaskByActivity(TEST_ACTIVITY).getTaskId();
490         for (int i = 0; i < intents.length; i++) {
491             taskIds[i + 1] = mWmState.getTaskByActivity(intents[i].getComponent()).getTaskId();
492         }
493         return taskIds;
494     }
495 }
496