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