• 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.app.cts;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
20 import static android.content.Context.ACTIVITY_SERVICE;
21 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
22 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
23 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
24 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
25 
26 import static org.junit.Assert.assertTrue;
27 import static org.junit.Assert.fail;
28 
29 import android.app.Activity;
30 import android.app.ActivityManager;
31 import android.app.ActivityOptions;
32 import android.app.Instrumentation;
33 import android.app.stubs.MockActivity;
34 import android.app.stubs.MockListActivity;
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.os.SystemClock;
39 
40 import androidx.test.InstrumentationRegistry;
41 import androidx.test.filters.FlakyTest;
42 import androidx.test.filters.MediumTest;
43 import androidx.test.rule.ActivityTestRule;
44 import androidx.test.runner.AndroidJUnit4;
45 import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
46 import androidx.test.runner.lifecycle.ActivityLifecycleMonitor;
47 import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
48 import androidx.test.runner.lifecycle.Stage;
49 
50 import org.junit.Before;
51 import org.junit.Rule;
52 import org.junit.Test;
53 import org.junit.runner.RunWith;
54 
55 import java.util.ArrayList;
56 import java.util.List;
57 import java.util.function.BooleanSupplier;
58 
59 /**
60  * atest CtsAppTestCases:AppTaskTests
61  */
62 @MediumTest
63 @FlakyTest(detail = "Can be promoted to pre-submit once confirmed stable.")
64 @RunWith(AndroidJUnit4.class)
65 public class AppTaskTests {
66 
67     private static final long TIME_SLICE_MS = 100;
68     private static final long MAX_WAIT_MS = 1500;
69 
70     private Instrumentation mInstrumentation;
71     private ActivityLifecycleMonitor mLifecycleMonitor;
72     private Context mTargetContext;
73 
74     @Rule
75     public ActivityTestRule<MockActivity> mActivityRule =
76             new ActivityTestRule<MockActivity>(MockActivity.class) {
77         @Override
78         public Intent getActivityIntent() {
79             Intent intent = new Intent();
80             intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT
81                     | FLAG_ACTIVITY_MULTIPLE_TASK);
82             return intent;
83         }
84     };
85 
86     @Before
setUp()87     public void setUp() throws Exception {
88         mInstrumentation = InstrumentationRegistry.getInstrumentation();
89         mLifecycleMonitor = ActivityLifecycleMonitorRegistry.getInstance();
90         mTargetContext = mInstrumentation.getTargetContext();
91         removeAllAppTasks();
92     }
93 
94     /**
95      * Launch an activity and ensure it is in the app task list.
96      */
97     @Test
testSingleAppTask()98     public void testSingleAppTask() throws Exception {
99         final Activity a1 = mActivityRule.launchActivity(null);
100         final List<ActivityManager.AppTask> appTasks = getAppTasks();
101         assertTrue(appTasks.size() == 1);
102         assertTrue(appTasks.get(0).getTaskInfo().topActivity.equals(a1.getComponentName()));
103     }
104 
105     /**
106      * Launch a couple tasks and ensure they are in the app tasks list.
107      */
108     @Test
testMultipleAppTasks()109     public void testMultipleAppTasks() throws Exception {
110         final ArrayList<Activity> activities = new ArrayList<>();
111         for (int i = 0; i < 5; i++) {
112             activities.add(mActivityRule.launchActivity(null));
113         }
114         final List<ActivityManager.AppTask> appTasks = getAppTasks();
115         assertTrue(appTasks.size() == activities.size());
116         for (int i = 0; i < appTasks.size(); i++) {
117             assertTrue(appTasks.get(i).getTaskInfo().topActivity.equals(
118                     activities.get(i).getComponentName()));
119         }
120     }
121 
122     /**
123      * Remove an app task and ensure that it is actually removed.
124      */
125     @Test
testFinishAndRemoveTask()126     public void testFinishAndRemoveTask() throws Exception {
127         final Activity a1 = mActivityRule.launchActivity(null);
128         waitAndAssertCondition(() -> getAppTasks().size() == 1, "Expected 1 running task");
129         getAppTask(a1).finishAndRemoveTask();
130         waitAndAssertCondition(() -> getAppTasks().isEmpty(), "Expected no running tasks");
131     }
132 
133     /**
134      * Ensure that moveToFront will bring the first activity forward.
135      */
136     @Test
testMoveToFront()137     public void testMoveToFront() throws Exception {
138         final Activity a1 = mActivityRule.launchActivity(null);
139 
140         // Launch fullscreen activity as an another task to hide the first activity
141         final Intent intent = new Intent();
142         intent.setComponent(new ComponentName(mTargetContext, MockActivity.class));
143         intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT
144                     | FLAG_ACTIVITY_MULTIPLE_TASK);
145         final ActivityOptions options = ActivityOptions.makeBasic();
146         options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
147         mInstrumentation.startActivitySync(intent, options.toBundle());
148 
149         final BooleanValue targetResumed = new BooleanValue();
150         mLifecycleMonitor.addLifecycleCallback(new ActivityLifecycleCallback() {
151             public void onActivityLifecycleChanged(Activity activity, Stage stage) {
152                 if (activity == a1 && stage == Stage.RESUMED) {
153                     targetResumed.value = true;
154                 }
155             }
156         });
157 
158         getAppTask(a1).moveToFront();
159         waitAndAssertCondition(() -> targetResumed.value,
160                 "Expected activity brought to front and resumed");
161     }
162 
163     /**
164      * Ensure that starting a new activity in the same task results in two activities in the task.
165      */
166     @Test
testStartActivityInTask_NoNewTask()167     public void testStartActivityInTask_NoNewTask() throws Exception {
168         final Activity a1 = mActivityRule.launchActivity(null);
169         final ActivityManager.AppTask task = getAppTask(a1);
170         final Intent intent = new Intent();
171         intent.setComponent(new ComponentName(mTargetContext, MockListActivity.class));
172         task.startActivity(mTargetContext, intent, null);
173         waitAndAssertCondition(
174                 () -> getAppTask(a1) != null && getAppTask(a1).getTaskInfo().numActivities == 2,
175                 "Waiting for activity launch");
176 
177         final ActivityManager.RecentTaskInfo taskInfo = task.getTaskInfo();
178         assertTrue(taskInfo.numActivities == 2);
179         assertTrue(taskInfo.baseActivity.equals(a1.getComponentName()));
180         assertTrue(taskInfo.topActivity.equals(intent.getComponent()));
181     }
182 
183     /**
184      * Ensure that an activity with FLAG_ACTIVITY_NEW_TASK causes the task to be brought forward
185      * and the new activity not being started.
186      */
187     @Test
testStartActivityInTask_NewTask()188     public void testStartActivityInTask_NewTask() throws Exception {
189         final Activity a1 = mActivityRule.launchActivity(null);
190         final ActivityManager.AppTask task = getAppTask(a1);
191         final Intent intent = new Intent();
192         intent.setComponent(new ComponentName(mTargetContext, MockActivity.class));
193         intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
194         task.startActivity(mTargetContext, intent, null);
195 
196         final ActivityManager.RecentTaskInfo taskInfo = task.getTaskInfo();
197         assertTrue(taskInfo.numActivities == 1);
198         assertTrue(taskInfo.baseActivity.equals(a1.getComponentName()));
199     }
200 
201     /**
202      * Ensure that the activity that is excluded from recents is reflected in the recent task info.
203      */
204     @Test
testSetExcludeFromRecents()205     public void testSetExcludeFromRecents() throws Exception {
206         final Activity a1 = mActivityRule.launchActivity(null);
207         final List<ActivityManager.AppTask> appTasks = getAppTasks();
208         final ActivityManager.AppTask t1 = appTasks.get(0);
209         t1.setExcludeFromRecents(true);
210         assertTrue((t1.getTaskInfo().baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
211                 != 0);
212         t1.setExcludeFromRecents(false);
213         assertTrue((t1.getTaskInfo().baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
214                 == 0);
215     }
216 
217     /**
218      * @return all the {@param ActivityManager.AppTask}s for the current app.
219      */
getAppTasks()220     private List<ActivityManager.AppTask> getAppTasks() {
221         ActivityManager am = (ActivityManager) mTargetContext.getSystemService(ACTIVITY_SERVICE);
222         return am.getAppTasks();
223     }
224 
225     /**
226      * @return the {@param ActivityManager.AppTask} for the associated activity.
227      */
getAppTask(Activity activity)228     private ActivityManager.AppTask getAppTask(Activity activity) {
229         waitAndAssertCondition(() -> getAppTask(getAppTasks(), activity) != null,
230                 "Waiting for app task");
231         return getAppTask(getAppTasks(), activity);
232     }
233 
getAppTask(List<ActivityManager.AppTask> appTasks, Activity activity)234     private ActivityManager.AppTask getAppTask(List<ActivityManager.AppTask> appTasks,
235             Activity activity) {
236         for (ActivityManager.AppTask task : appTasks) {
237             if (task.getTaskInfo().taskId == activity.getTaskId()) {
238                 return task;
239             }
240         }
241         return null;
242     }
243 
244     /**
245      * Removes all the app tasks the test app.
246      */
removeAllAppTasks()247     private void removeAllAppTasks() {
248         final List<ActivityManager.AppTask> appTasks = getAppTasks();
249         for (ActivityManager.AppTask task : appTasks) {
250             task.finishAndRemoveTask();
251         }
252         waitAndAssertCondition(() -> getAppTasks().isEmpty(),
253                 "Expected no app tasks after all removed");
254     }
255 
waitAndAssertCondition(BooleanSupplier condition, String failMsgContext)256     private void waitAndAssertCondition(BooleanSupplier condition, String failMsgContext) {
257         long startTime = SystemClock.elapsedRealtime();
258         while (true) {
259             if (condition.getAsBoolean()) {
260                 // Condition passed
261                 return;
262             } else if (SystemClock.elapsedRealtime() > (startTime + MAX_WAIT_MS)) {
263                 // Timed out
264                 fail("Timed out waiting for: " + failMsgContext);
265             } else {
266                 SystemClock.sleep(TIME_SLICE_MS);
267             }
268         }
269     }
270 
271     private class BooleanValue {
272         boolean value;
273     }
274 }