1 /* 2 * Copyright (C) 2023 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.AppOpsManager.MODE_ERRORED; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 22 import static android.server.wm.ComponentNameUtils.getActivityName; 23 import static android.server.wm.backgroundactivity.common.CommonComponents.COMMON_FOREGROUND_ACTIVITY_EXTRAS; 24 25 import static com.android.compatibility.common.util.SystemUtil.runShellCommand; 26 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; 27 28 import static com.google.common.truth.Truth.assertWithMessage; 29 30 import static org.junit.Assert.assertEquals; 31 import static org.junit.Assert.assertNull; 32 33 import android.content.ComponentName; 34 import android.content.Intent; 35 import android.content.ServiceConnection; 36 import android.os.UserManager; 37 import android.server.wm.WindowManagerState.Task; 38 import android.util.Log; 39 40 import androidx.annotation.CallSuper; 41 42 import com.android.compatibility.common.util.AppOpsUtils; 43 44 import org.junit.After; 45 import org.junit.Before; 46 47 import java.util.Arrays; 48 import java.util.List; 49 import java.util.stream.Collectors; 50 import java.util.stream.Stream; 51 52 public abstract class BackgroundActivityTestBase extends ActivityManagerTestBase { 53 54 private static final String TAG = BackgroundActivityTestBase.class.getSimpleName(); 55 56 static final String APP_A_PACKAGE = "android.server.wm.backgroundactivity.appa"; 57 static final android.server.wm.backgroundactivity.appa.Components APP_A = 58 android.server.wm.backgroundactivity.appa.Components.get(APP_A_PACKAGE); 59 static final android.server.wm.backgroundactivity.appa.Components APP_A_33 = 60 android.server.wm.backgroundactivity.appa.Components.get(APP_A_PACKAGE + "33"); 61 62 static final String APP_B_PACKAGE = "android.server.wm.backgroundactivity.appb"; 63 static final android.server.wm.backgroundactivity.appb.Components APP_B = 64 android.server.wm.backgroundactivity.appb.Components.get(APP_B_PACKAGE); 65 static final android.server.wm.backgroundactivity.appb.Components APP_B_33 = 66 android.server.wm.backgroundactivity.appb.Components.get(APP_B_PACKAGE + "33"); 67 68 static final List<android.server.wm.backgroundactivity.appa.Components> ALL_A = 69 List.of(APP_A, APP_A_33); 70 static final List<android.server.wm.backgroundactivity.appb.Components> ALL_B = 71 List.of(APP_B, APP_B_33); 72 73 static final String SHELL_PACKAGE = "com.android.shell"; 74 static final int ACTIVITY_FOCUS_TIMEOUT_MS = 3000; 75 76 // TODO(b/258792202): Cleanup with feature flag 77 static final String NAMESPACE_WINDOW_MANAGER = "window_manager"; 78 79 ServiceConnection mBalServiceConnection; 80 81 @Override 82 @Before 83 @CallSuper setUp()84 public void setUp() throws Exception { 85 // disable SAW appopp for AppA (it's granted automatically when installed in CTS) 86 for (android.server.wm.backgroundactivity.appa.Components components : ALL_A) { 87 AppOpsUtils.setOpMode(components.APP_PACKAGE_NAME, "android:system_alert_window", 88 MODE_ERRORED); 89 assertEquals(AppOpsUtils.getOpMode(components.APP_PACKAGE_NAME, 90 "android:system_alert_window"), 91 MODE_ERRORED); 92 } 93 94 super.setUp(); 95 96 for (android.server.wm.backgroundactivity.appa.Components appA : ALL_A) { 97 assertNull(mWmState.getTaskByActivity(appA.BACKGROUND_ACTIVITY)); 98 assertNull(mWmState.getTaskByActivity(appA.FOREGROUND_ACTIVITY)); 99 runShellCommand("cmd deviceidle tempwhitelist -d 100000 " 100 + appA.APP_PACKAGE_NAME); 101 } 102 for (android.server.wm.backgroundactivity.appb.Components appB : ALL_B) { 103 assertNull(mWmState.getTaskByActivity(appB.FOREGROUND_ACTIVITY)); 104 runShellCommand("cmd deviceidle tempwhitelist -d 100000 " 105 + appB.APP_PACKAGE_NAME); 106 } 107 } 108 109 @After tearDown()110 public void tearDown() throws Exception { 111 // We do this before anything else, because having an active device owner can prevent us 112 // from being able to force stop apps. (b/142061276) 113 for (android.server.wm.backgroundactivity.appa.Components appA : ALL_A) { 114 runWithShellPermissionIdentity(() -> { 115 runShellCommand("dpm remove-active-admin --user 0 " 116 + appA.SIMPLE_ADMIN_RECEIVER.flattenToString()); 117 if (UserManager.isHeadlessSystemUserMode()) { 118 // Must also remove the PO from current user 119 runShellCommand("dpm remove-active-admin --user cur " 120 + appA.SIMPLE_ADMIN_RECEIVER.flattenToString()); 121 } 122 }); 123 stopTestPackage(appA.APP_PACKAGE_NAME); 124 AppOpsUtils.reset(appA.APP_PACKAGE_NAME); 125 126 } 127 for (android.server.wm.backgroundactivity.appb.Components appB : ALL_B) { 128 stopTestPackage(appB.APP_PACKAGE_NAME); 129 } 130 AppOpsUtils.reset(SHELL_PACKAGE); 131 if (mBalServiceConnection != null) { 132 mContext.unbindService(mBalServiceConnection); 133 } 134 } 135 waitForActivityFocused(ComponentName componentName)136 boolean waitForActivityFocused(ComponentName componentName) { 137 return waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS, componentName); 138 } 139 assertPinnedStackDoesNotExist()140 void assertPinnedStackDoesNotExist() { 141 mWmState.assertDoesNotContainStack("Must not contain pinned stack.", 142 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); 143 } assertTaskStackIsEmpty(ComponentName sourceComponent)144 void assertTaskStackIsEmpty(ComponentName sourceComponent) { 145 Task task = mWmState.getTaskByActivity(sourceComponent); 146 assertWithMessage("task for %s", sourceComponent.flattenToShortString()).that(task) 147 .isNull(); 148 } 149 assertTaskStackHasComponents(ComponentName sourceComponent, ComponentName... expectedComponents)150 void assertTaskStackHasComponents(ComponentName sourceComponent, 151 ComponentName... expectedComponents) { 152 Task task = mWmState.getTaskByActivity(sourceComponent); 153 assertWithMessage("task for %s", sourceComponent.flattenToShortString()).that(task) 154 .isNotNull(); 155 Log.d(TAG, "Task for " + sourceComponent.flattenToShortString() + ": " + task 156 + " Activities: " + task.mActivities); 157 List<String> actualNames = getActivityNames(task.mActivities); 158 List<String> expectedNames = Arrays.stream(expectedComponents) 159 .map((c) -> c.flattenToShortString()).collect(Collectors.toList()); 160 161 assertWithMessage("task activities").that(actualNames) 162 .containsExactlyElementsIn(expectedNames).inOrder(); 163 } 164 assertTaskDoesNotHaveVisibleComponents(ComponentName sourceComponent, ComponentName... expectedComponents)165 void assertTaskDoesNotHaveVisibleComponents(ComponentName sourceComponent, 166 ComponentName... expectedComponents) { 167 Task task = mWmState.getTaskByActivity(sourceComponent); 168 Log.d(TAG, "Task for " + sourceComponent.flattenToShortString() + ": " + task); 169 List<WindowManagerState.Activity> actual = getVisibleActivities(task.mActivities); 170 Log.v(TAG, "Task activities: all=" + task.mActivities + ", visible=" + actual); 171 if (actual == null) { 172 return; 173 } 174 List<String> actualNames = getActivityNames(actual); 175 List<String> expectedNames = Arrays.stream(expectedComponents) 176 .map((c) -> c.flattenToShortString()).collect(Collectors.toList()); 177 178 assertWithMessage("task activities").that(actualNames).containsNoneIn(expectedNames); 179 } 180 getVisibleActivities( List<WindowManagerState.Activity> activities)181 List<WindowManagerState.Activity> getVisibleActivities( 182 List<WindowManagerState.Activity> activities) { 183 return activities.stream().filter(WindowManagerState.Activity::isVisible) 184 .collect(Collectors.toList()); 185 } 186 getActivityNames(List<WindowManagerState.Activity> activities)187 List<String> getActivityNames(List<WindowManagerState.Activity> activities) { 188 return activities.stream().map(a -> a.getName()).collect(Collectors.toList()); 189 } 190 getLaunchActivitiesBroadcast(android.server.wm.backgroundactivity.appa.Components appA, ComponentName... componentNames)191 Intent getLaunchActivitiesBroadcast(android.server.wm.backgroundactivity.appa.Components appA, 192 ComponentName... componentNames) { 193 Intent broadcastIntent = new Intent( 194 appA.FOREGROUND_ACTIVITY_ACTIONS.LAUNCH_BACKGROUND_ACTIVITIES); 195 Intent[] intents = Stream.of(componentNames) 196 .map(c -> { 197 Intent intent = new Intent(); 198 intent.setComponent(c); 199 return intent; 200 }) 201 .toArray(Intent[]::new); 202 broadcastIntent.putExtra(appA.FOREGROUND_ACTIVITY_EXTRA.LAUNCH_INTENTS, intents); 203 return broadcastIntent; 204 } 205 206 class ActivityStartVerifier { 207 private Intent mBroadcastIntent = new Intent(); 208 private Intent mLaunchIntent = new Intent(); 209 setupTaskWithForegroundActivity( android.server.wm.backgroundactivity.appa.Components appA)210 ActivityStartVerifier setupTaskWithForegroundActivity( 211 android.server.wm.backgroundactivity.appa.Components appA) { 212 setupTaskWithForegroundActivity(appA, -1); 213 return this; 214 } 215 setupTaskWithForegroundActivity( android.server.wm.backgroundactivity.appa.Components appA, int id)216 ActivityStartVerifier setupTaskWithForegroundActivity( 217 android.server.wm.backgroundactivity.appa.Components appA, int id) { 218 Intent intent = new Intent(); 219 intent.setComponent(appA.FOREGROUND_ACTIVITY); 220 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 221 intent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.ACTIVITY_ID, id); 222 mContext.startActivity(intent); 223 mWmState.waitForValidState(appA.FOREGROUND_ACTIVITY); 224 return this; 225 } 226 setupTaskWithEmbeddingActivity( android.server.wm.backgroundactivity.appa.Components appA)227 ActivityStartVerifier setupTaskWithEmbeddingActivity( 228 android.server.wm.backgroundactivity.appa.Components appA) { 229 Intent intent = new Intent(); 230 intent.setComponent(appA.FOREGROUND_EMBEDDING_ACTIVITY); 231 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 232 mContext.startActivity(intent); 233 mWmState.waitForValidState(appA.FOREGROUND_EMBEDDING_ACTIVITY); 234 return this; 235 } 236 startFromForegroundActivity( android.server.wm.backgroundactivity.appa.Components appA)237 ActivityStartVerifier startFromForegroundActivity( 238 android.server.wm.backgroundactivity.appa.Components appA) { 239 mBroadcastIntent.setAction( 240 appA.FOREGROUND_ACTIVITY_ACTIONS.LAUNCH_BACKGROUND_ACTIVITIES); 241 return this; 242 } 243 startFromForegroundActivity( android.server.wm.backgroundactivity.appb.Components appB)244 ActivityStartVerifier startFromForegroundActivity( 245 android.server.wm.backgroundactivity.appb.Components appB) { 246 mBroadcastIntent.setAction( 247 appB.FOREGROUND_ACTIVITY_ACTIONS.LAUNCH_BACKGROUND_ACTIVITIES); 248 return this; 249 } 250 startFromForegroundActivity( android.server.wm.backgroundactivity.appa.Components appA, int id)251 ActivityStartVerifier startFromForegroundActivity( 252 android.server.wm.backgroundactivity.appa.Components appA, int id) { 253 startFromForegroundActivity(appA); 254 mBroadcastIntent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.ACTIVITY_ID, id); 255 return this; 256 } 257 startFromForegroundActivity( android.server.wm.backgroundactivity.appb.Components appB, int id)258 ActivityStartVerifier startFromForegroundActivity( 259 android.server.wm.backgroundactivity.appb.Components appB, int id) { 260 startFromForegroundActivity(appB); 261 mBroadcastIntent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.ACTIVITY_ID, id); 262 return this; 263 } 264 startFromEmbeddingActivity( android.server.wm.backgroundactivity.appa.Components appA)265 ActivityStartVerifier startFromEmbeddingActivity( 266 android.server.wm.backgroundactivity.appa.Components appA) { 267 mBroadcastIntent.setAction( 268 appA.FOREGROUND_EMBEDDING_ACTIVITY_ACTIONS.LAUNCH_EMBEDDED_ACTIVITY); 269 return this; 270 } 271 withBroadcastExtra(String key, boolean value)272 ActivityStartVerifier withBroadcastExtra(String key, boolean value) { 273 mBroadcastIntent.putExtra(key, value); 274 return this; 275 } 276 activity(ComponentName to)277 ActivityStartVerifier activity(ComponentName to) { 278 mLaunchIntent.setComponent(to); 279 mBroadcastIntent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.LAUNCH_INTENTS, 280 new Intent[]{mLaunchIntent}); 281 return this; 282 } 283 activity(ComponentName to, int id)284 ActivityStartVerifier activity(ComponentName to, int id) { 285 activity(to); 286 mLaunchIntent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.ACTIVITY_ID, id); 287 return this; 288 } 289 290 /** 291 * Broadcasts the specified intents, asserts that the launch succeeded or failed, then 292 * resets all ActivityStartVerifier state (i.e - intent component and flags) so the 293 * ActivityStartVerifier can be reused. 294 */ executeAndAssertLaunch(boolean succeeds)295 ActivityStartVerifier executeAndAssertLaunch(boolean succeeds) { 296 mContext.sendBroadcast(mBroadcastIntent); 297 298 ComponentName launchedComponent = mLaunchIntent.getComponent(); 299 mWmState.waitForValidState(launchedComponent); 300 boolean result = waitForActivityFocused(launchedComponent); 301 assertEquals("Activity: " + launchedComponent.flattenToShortString() + " launch ", 302 succeeds, result); 303 304 // Reset intents to remove any added flags 305 reset(); 306 return this; 307 } 308 reset()309 void reset() { 310 mBroadcastIntent = new Intent(); 311 mLaunchIntent = new Intent(); 312 } 313 thenAssert(Runnable run)314 ActivityStartVerifier thenAssert(Runnable run) { 315 run.run(); 316 return this; 317 } 318 thenAssertTaskStack(ComponentName... expectedComponents)319 ActivityStartVerifier thenAssertTaskStack(ComponentName... expectedComponents) { 320 assertTaskStackHasComponents(expectedComponents[expectedComponents.length - 1], 321 expectedComponents); 322 return this; 323 } 324 325 /** 326 * <pre> 327 * | expectedRootActivity | expectedEmbeddedActivities | 328 * | fragment 1 - left | fragment 0 - right | 329 * |----------------------|----------------------------| 330 * | | A4 | top 331 * | | A3 | 332 * | A1 | A2 | bottom 333 * </pre> 334 * @param expectedEmbeddedActivities The expected activities on the right side of the split 335 * (fragment 0), top to bottom 336 * @param expectedRootActivity The expected activity on the left side of the split 337 * (fragment 1) 338 */ thenAssertEmbeddingTaskStack( ComponentName[] expectedEmbeddedActivities, ComponentName expectedRootActivity)339 ActivityStartVerifier thenAssertEmbeddingTaskStack( 340 ComponentName[] expectedEmbeddedActivities, ComponentName expectedRootActivity) { 341 List<WindowManagerState.TaskFragment> fragments = mWmState.getTaskByActivity( 342 expectedRootActivity).getTaskFragments(); 343 assertEquals(2, fragments.size()); 344 345 List<WindowManagerState.Activity> embeddedActivities = fragments.get(0).getActivities(); 346 List<WindowManagerState.Activity> rootActivity = fragments.get(1).getActivities(); 347 348 assertEquals(1, rootActivity.size()); 349 assertEquals(expectedRootActivity.flattenToShortString(), 350 rootActivity.get(0).getName()); 351 352 assertEquals(expectedEmbeddedActivities.length, embeddedActivities.size()); 353 for (int i = 0; i < expectedEmbeddedActivities.length; i++) { 354 assertEquals(expectedEmbeddedActivities[i].flattenToShortString(), 355 embeddedActivities.get(i).getName()); 356 } 357 return this; 358 } 359 } 360 361 assertActivityFocused(ComponentName componentName)362 protected void assertActivityFocused(ComponentName componentName) { 363 assertActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS, componentName); 364 } 365 assertActivityNotFocused(ComponentName componentName)366 protected void assertActivityNotFocused(ComponentName componentName) { 367 assertActivityNotFocused(ACTIVITY_FOCUS_TIMEOUT_MS, componentName); 368 } 369 assertActivityFocused(ComponentName componentName, String message)370 protected void assertActivityFocused(ComponentName componentName, String message) { 371 assertActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS, componentName, message); 372 } 373 assertActivityNotFocused(ComponentName componentName, String message)374 protected void assertActivityNotFocused(ComponentName componentName, String message) { 375 assertActivityNotFocused(ACTIVITY_FOCUS_TIMEOUT_MS, componentName, message); 376 } 377 378 /** Asserts the activity is focused before timeout. */ assertActivityFocused(int timeoutMs, ComponentName componentName)379 protected void assertActivityFocused(int timeoutMs, ComponentName componentName) { 380 assertActivityFocused(timeoutMs, componentName, 381 "activity should be focused within " + timeoutMs + "ms"); 382 } 383 384 /** Asserts the activity is not focused until timeout. */ assertActivityNotFocused(int timeoutMs, ComponentName componentName)385 protected void assertActivityNotFocused(int timeoutMs, ComponentName componentName) { 386 assertActivityNotFocused(timeoutMs, componentName, 387 "activity should not be focused within " + timeoutMs + "ms"); 388 } 389 390 /** Asserts the activity is focused before timeout. */ assertActivityFocused(int timeoutMs, ComponentName componentName, String message)391 protected void assertActivityFocused(int timeoutMs, ComponentName componentName, 392 String message) { 393 waitForActivityResumed(timeoutMs, componentName); 394 assertWithMessage(message).that(mWmState.getFocusedActivity()).isEqualTo( 395 getActivityName(componentName)); 396 } 397 398 /** Asserts the activity is not focused until timeout. */ assertActivityNotFocused(int timeoutMs, ComponentName componentName, String message)399 protected void assertActivityNotFocused(int timeoutMs, ComponentName componentName, 400 String message) { 401 waitForActivityResumed(timeoutMs, componentName); 402 assertWithMessage(message).that(mWmState.getFocusedActivity()) 403 .isNotEqualTo(getActivityName(componentName)); 404 } 405 406 } 407