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