1 /* 2 * Copyright (C) 2024 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 org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assert.fail; 24 25 import android.app.ActivityManager; 26 import android.app.ApplicationStartInfo; 27 import android.app.Flags; 28 import android.app.Instrumentation; 29 import android.content.BroadcastReceiver; 30 import android.content.ComponentName; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.pm.PackageManager; 35 import android.os.Bundle; 36 import android.os.Process; 37 import android.os.UserHandle; 38 import android.platform.test.annotations.RequiresFlagsEnabled; 39 import android.util.Log; 40 41 import androidx.test.ext.junit.runners.AndroidJUnit4; 42 import androidx.test.platform.app.InstrumentationRegistry; 43 44 import com.android.compatibility.common.util.AmUtils; 45 import com.android.compatibility.common.util.ShellIdentityUtils; 46 import com.android.compatibility.common.util.SystemUtil; 47 48 import com.google.errorprone.annotations.FormatMethod; 49 50 import org.junit.After; 51 import org.junit.Before; 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.Map; 58 59 @RunWith(AndroidJUnit4.class) 60 public final class ActivityManagerAppStartInfoTest { 61 private static final String TAG = ActivityManagerAppStartInfoTest.class.getSimpleName(); 62 63 // Begin section: keep in sync with {@link ApiTestActivity} 64 private static final String REQUEST_KEY_ACTION = "action"; 65 private static final String REQUEST_KEY_TIMESTAMP_KEY_FIRST = "timestamp_key_first"; 66 private static final String REQUEST_KEY_TIMESTAMP_VALUE_FIRST = "timestamp_value_first"; 67 private static final String REQUEST_KEY_TIMESTAMP_KEY_LAST = "timestamp_key_last"; 68 private static final String REQUEST_KEY_TIMESTAMP_VALUE_LAST = "timestamp_value_last"; 69 70 private static final int REQUEST_VALUE_QUERY_START = 1; 71 private static final int REQUEST_VALUE_ADD_TIMESTAMP = 2; 72 private static final int REQUEST_VALUE_LISTENER_ADD_ONE = 3; 73 private static final int REQUEST_VALUE_LISTENER_ADD_MULTIPLE = 4; 74 private static final int REQUEST_VALUE_LISTENER_ADD_REMOVE = 5; 75 private static final int REQUEST_VALUE_CRASH = 6; 76 77 private static final String REPLY_ACTION_COMPLETE = 78 "com.android.cts.startinfoapp.ACTION_COMPLETE"; 79 80 private static final String REPLY_EXTRA_STATUS_KEY = "status"; 81 82 private static final int REPLY_EXTRA_SUCCESS_VALUE = 1; 83 //private static final int REPLY_EXTRA_FAILURE_VALUE = 2; 84 85 private static final int REPLY_STATUS_NONE = -1; 86 // End section: keep in sync with {@link ApiTestActivity} 87 88 private static final String STUB_APK = 89 "/data/local/tmp/cts/content/CtsAppStartInfoApp.apk"; 90 private static final String STUB_PACKAGE_NAME = "com.android.cts.startinfoapp"; 91 private static final String SIMPLE_ACTIVITY = ".ApiTestActivity"; 92 93 private static final int FIRST_TIMESTAMP_KEY = 94 ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START; 95 private static final int LAST_TIMESTAMP_KEY = 96 ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER; 97 98 private static final int MAX_WAITS_FOR_START = 20; 99 private static final int WAIT_FOR_START_MS = 400; 100 101 // Return states of the ResultReceiverFilter. 102 public static final int RESULT_PASS = 1; 103 public static final int RESULT_FAIL = 2; 104 public static final int RESULT_TIMEOUT = 3; 105 106 private Context mContext; 107 private Instrumentation mInstrumentation; 108 private ActivityManager mActivityManager; 109 private PackageManager mPackageManager; 110 111 private int mStubPackageUid; 112 private int mCurrentUserId; 113 114 @Before setUp()115 public void setUp() throws Exception { 116 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 117 mContext = mInstrumentation.getContext(); 118 mActivityManager = mContext.getSystemService(ActivityManager.class); 119 mPackageManager = mContext.getPackageManager(); 120 mCurrentUserId = UserHandle.getUserId(Process.myUid()); 121 122 executeShellCmd("pm install -r --force-queryable " + STUB_APK); 123 124 mStubPackageUid = mPackageManager.getPackageUid(STUB_PACKAGE_NAME, 0); 125 } 126 127 @After tearDown()128 public void tearDown() throws Exception { 129 executeShellCmd("am force-stop " + STUB_PACKAGE_NAME); 130 mInstrumentation.getUiAutomation().dropShellPermissionIdentity(); 131 } 132 133 @Test 134 @RequiresFlagsEnabled(Flags.FLAG_APP_START_INFO) testLauncherStart()135 public void testLauncherStart() throws Exception { 136 clearHistoricalStartInfo(); 137 138 Intent intent = 139 mPackageManager.getLaunchIntentForPackage(STUB_PACKAGE_NAME); 140 mContext.startActivity(intent); 141 142 ApplicationStartInfo info = waitForAppStart(); 143 144 verify(info, STUB_PACKAGE_NAME, STUB_PACKAGE_NAME, intent, 145 ApplicationStartInfo.START_REASON_LAUNCHER, 146 ApplicationStartInfo.START_TYPE_COLD, 147 ApplicationStartInfo.LAUNCH_MODE_STANDARD, 148 ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN); 149 150 verifyIds(info, 0, mStubPackageUid, mStubPackageUid, mStubPackageUid); 151 } 152 153 @Test 154 @RequiresFlagsEnabled(Flags.FLAG_APP_START_INFO) testActivityStart()155 public void testActivityStart() throws Exception { 156 clearHistoricalStartInfo(); 157 158 executeShellCmd("am start -n " + STUB_PACKAGE_NAME + "/" + STUB_PACKAGE_NAME 159 + SIMPLE_ACTIVITY); 160 161 ApplicationStartInfo info = waitForAppStart(); 162 163 Intent intent = new Intent(); 164 intent.setComponent(ComponentName.createRelative(STUB_PACKAGE_NAME, 165 SIMPLE_ACTIVITY)); 166 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 167 168 verify(info, STUB_PACKAGE_NAME, STUB_PACKAGE_NAME, intent, 169 ApplicationStartInfo.START_REASON_START_ACTIVITY, 170 ApplicationStartInfo.START_TYPE_COLD, 171 ApplicationStartInfo.LAUNCH_MODE_STANDARD, 172 ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN); 173 174 verifyIds(info, 0, mStubPackageUid, mStubPackageUid, mStubPackageUid); 175 } 176 177 /** Test that the wasForceStopped state is accurate in force stopped case. */ 178 @Test 179 @RequiresFlagsEnabled(Flags.FLAG_APP_START_INFO) testWasForceStopped()180 public void testWasForceStopped() throws Exception { 181 clearHistoricalStartInfo(); 182 183 // Start the test app and wait for it to complete 184 executeShellCmd("am start -n " + STUB_PACKAGE_NAME + "/" + STUB_PACKAGE_NAME 185 + SIMPLE_ACTIVITY); 186 waitForAppStart(); 187 188 // Now force stop the app 189 executeShellCmd("am force-stop " + STUB_PACKAGE_NAME); 190 191 // Clear records again, we don't want to check the previous one. 192 clearHistoricalStartInfo(); 193 194 // Start the app again 195 executeShellCmd("am start -n " + STUB_PACKAGE_NAME + "/" + STUB_PACKAGE_NAME 196 + SIMPLE_ACTIVITY); 197 198 // Obtain the start record and confirm it shows having been force stopped 199 ApplicationStartInfo info = waitForAppStart(); 200 assertTrue(info.wasForceStopped()); 201 } 202 203 /** Test that the wasForceStopped state is accurate in not force stopped case. */ 204 @Test 205 @RequiresFlagsEnabled(Flags.FLAG_APP_START_INFO) testWasNotForceStopped()206 public void testWasNotForceStopped() throws Exception { 207 clearHistoricalStartInfo(); 208 209 // Start the test app and wait for it to complete 210 executeShellCmd("am start -n " + STUB_PACKAGE_NAME + "/" + STUB_PACKAGE_NAME 211 + SIMPLE_ACTIVITY); 212 waitForAppStart(); 213 214 // Now force stop the app 215 executeShellCmd("am force-stop " + STUB_PACKAGE_NAME); 216 217 // Clear records again, we don't want to check the previous one here. 218 clearHistoricalStartInfo(); 219 220 // Start the app with flag to immediately exit 221 executeShellCmd("am start -n %s/%s%s --ei %s %d", 222 STUB_PACKAGE_NAME, STUB_PACKAGE_NAME, SIMPLE_ACTIVITY, // package/activity to start 223 REQUEST_KEY_ACTION, REQUEST_VALUE_CRASH); // action to perform 224 sleep(1000); 225 226 // Clear records again, we don't want to check the previous one. 227 clearHistoricalStartInfo(); 228 229 // Start the app again 230 executeShellCmd("am start -n " + STUB_PACKAGE_NAME + "/" + STUB_PACKAGE_NAME 231 + SIMPLE_ACTIVITY); 232 233 // Obtain the start record and confirm it shows having not been force stopped 234 ApplicationStartInfo info = waitForAppStart(); 235 assertFalse(info.wasForceStopped()); 236 } 237 238 /** 239 * Start an app and make sure its record exists, then verify 240 * the record is removed when the app is uninstalled. 241 */ 242 @Test 243 @RequiresFlagsEnabled(Flags.FLAG_APP_START_INFO) testAppRemoved()244 public void testAppRemoved() throws Exception { 245 testActivityStart(); 246 247 executeShellCmd("pm uninstall " + STUB_PACKAGE_NAME); 248 249 List<ApplicationStartInfo> list = 250 ShellIdentityUtils.invokeMethodWithShellPermissions( 251 STUB_PACKAGE_NAME, 1, 252 mActivityManager::getExternalHistoricalProcessStartReasons, 253 android.Manifest.permission.DUMP); 254 255 assertTrue(list != null && list.size() == 0); 256 } 257 258 /** 259 * Test querying the startup of the process we're currently in. 260 */ 261 @Test 262 @RequiresFlagsEnabled(Flags.FLAG_APP_START_INFO) testQueryThisProcess()263 public void testQueryThisProcess() throws Exception { 264 clearHistoricalStartInfo(); 265 266 ResultReceiverFilter receiver = new ResultReceiverFilter(REPLY_ACTION_COMPLETE, 1); 267 268 // Start the app and have it query its own start info record. 269 executeShellCmd("am start -n %s/%s%s --ei %s %d", 270 STUB_PACKAGE_NAME, STUB_PACKAGE_NAME, SIMPLE_ACTIVITY, // package/activity to start 271 REQUEST_KEY_ACTION, REQUEST_VALUE_QUERY_START); // action to perform 272 273 // Wait for complete callback 274 assertEquals(RESULT_PASS, receiver.waitForActivity()); 275 receiver.close(); 276 277 // Confirm that the app confirmed that it successfully obtained record. 278 assertEquals(1, receiver.mIntents.size()); 279 280 Bundle extras = receiver.mIntents.get(0).getExtras(); 281 assertNotNull(extras); 282 283 int status = extras.getInt(REPLY_EXTRA_STATUS_KEY, -1); 284 assertEquals(REPLY_EXTRA_SUCCESS_VALUE, status); 285 } 286 287 /** 288 * Test adding timestamps and verify that the timestamps that were added are still there on a 289 * subsequent query. 290 * 291 * Timestamp is created by test runner process and provided to test app to add to start record 292 * as apps can only add timestamps to their own starts. The subsequent query is performed here 293 * in the test app as querying records can be done from other processes and querying the process 294 * itself is not being tested here. 295 */ 296 @Test 297 @RequiresFlagsEnabled(Flags.FLAG_APP_START_INFO) testAddingTimestamps()298 public void testAddingTimestamps() throws Exception { 299 clearHistoricalStartInfo(); 300 301 final long timestampFirst = System.nanoTime(); 302 final long timestampLast = timestampFirst + 1000L; 303 ResultReceiverFilter receiver = new ResultReceiverFilter(REPLY_ACTION_COMPLETE, 1); 304 305 // Start the app and have it add the provided timestamp to its start record. 306 executeShellCmd( 307 "am start -n %s/%s%s --ei %s %d --ei %s %d --el %s %d --ei %s %d --el %s %d", 308 STUB_PACKAGE_NAME, STUB_PACKAGE_NAME, SIMPLE_ACTIVITY, // package/activity to start 309 REQUEST_KEY_ACTION, REQUEST_VALUE_ADD_TIMESTAMP, // action to perform 310 REQUEST_KEY_TIMESTAMP_KEY_FIRST, FIRST_TIMESTAMP_KEY, // first timestamp key 311 REQUEST_KEY_TIMESTAMP_VALUE_FIRST, timestampFirst, // first timestamp value 312 REQUEST_KEY_TIMESTAMP_KEY_LAST, LAST_TIMESTAMP_KEY, // last timestamp key 313 REQUEST_KEY_TIMESTAMP_VALUE_LAST, timestampLast); // last timestamp value 314 315 // Wait for complete callback 316 assertEquals(RESULT_PASS, receiver.waitForActivity()); 317 receiver.close(); 318 319 // Get the most recent app start 320 ApplicationStartInfo info = waitForAppStart(); 321 assertNotNull(info); 322 323 // Verify that the timestamps are retrievable and they're the same 324 // when we pull them back out. 325 Map<Integer, Long> timestamps = info.getStartupTimestamps(); 326 long timestampFirstFromInfo = timestamps.get(FIRST_TIMESTAMP_KEY); 327 long timestampLastFromInfo = timestamps.get(LAST_TIMESTAMP_KEY); 328 329 assertNotNull(timestampFirstFromInfo); 330 assertNotNull(timestampLastFromInfo); 331 332 assertEquals(timestampFirst, timestampFirstFromInfo); 333 assertEquals(timestampLast, timestampLastFromInfo); 334 } 335 336 /** 337 * Test that registered listeners are triggered when AppStartInfo is complete. 338 */ 339 @Test 340 @RequiresFlagsEnabled(Flags.FLAG_APP_START_INFO) testTriggerListeners()341 public void testTriggerListeners() throws Exception { 342 clearHistoricalStartInfo(); 343 344 ResultReceiverFilter receiver = new ResultReceiverFilter(REPLY_ACTION_COMPLETE, 1); 345 346 executeShellCmd("am start -n %s/%s%s --ei %s %d", 347 STUB_PACKAGE_NAME, STUB_PACKAGE_NAME, SIMPLE_ACTIVITY, // package/activity to start 348 REQUEST_KEY_ACTION, REQUEST_VALUE_LISTENER_ADD_ONE); // action to perform 349 350 // Wait for complete callback 351 assertEquals(RESULT_PASS, receiver.waitForActivity()); 352 receiver.close(); 353 354 // Confirm that the app confirmed that it successfully received a callback. 355 assertEquals(1, receiver.mIntents.size()); 356 357 Bundle extras = receiver.mIntents.get(0).getExtras(); 358 assertNotNull(extras); 359 360 int status = extras.getInt(REPLY_EXTRA_STATUS_KEY, REPLY_STATUS_NONE); 361 assertEquals(REPLY_EXTRA_SUCCESS_VALUE, status); 362 } 363 364 /** 365 * Test that multiple registered listeners are triggered when AppStartInfo is complete. 366 */ 367 @Test 368 @RequiresFlagsEnabled(Flags.FLAG_APP_START_INFO) testTriggerMultipleListeners()369 public void testTriggerMultipleListeners() throws Exception { 370 clearHistoricalStartInfo(); 371 372 ResultReceiverFilter receiver = new ResultReceiverFilter(REPLY_ACTION_COMPLETE, 2); 373 374 executeShellCmd("am start -n %s/%s%s --ei %s %d", 375 STUB_PACKAGE_NAME, STUB_PACKAGE_NAME, SIMPLE_ACTIVITY, // package/activity to start 376 REQUEST_KEY_ACTION, 377 REQUEST_VALUE_LISTENER_ADD_MULTIPLE); // action to perform 378 379 // Wait for complete callback 380 assertEquals(RESULT_PASS, receiver.waitForActivity()); 381 receiver.close(); 382 383 // Confirm that the app confirmed that it successfully received a callback. 384 assertEquals(2, receiver.mIntents.size()); 385 386 Bundle extras = receiver.mIntents.get(0).getExtras(); 387 assertNotNull(extras); 388 389 int status = extras.getInt(REPLY_EXTRA_STATUS_KEY, REPLY_STATUS_NONE); 390 assertEquals(REPLY_EXTRA_SUCCESS_VALUE, status); 391 392 extras = receiver.mIntents.get(1).getExtras(); 393 assertNotNull(extras); 394 395 status = extras.getInt(REPLY_EXTRA_STATUS_KEY, REPLY_STATUS_NONE); 396 assertEquals(REPLY_EXTRA_SUCCESS_VALUE, status); 397 } 398 399 /** 400 * Test that a removed listener is not triggered when AppStartInfo is complete. 401 */ 402 @Test 403 @RequiresFlagsEnabled(Flags.FLAG_APP_START_INFO) testRemoveListener()404 public void testRemoveListener() throws Exception { 405 clearHistoricalStartInfo(); 406 407 ResultReceiverFilter receiver = new ResultReceiverFilter(REPLY_ACTION_COMPLETE, 2); 408 409 executeShellCmd("am start -n %s/%s%s --ei %s %d", 410 STUB_PACKAGE_NAME, STUB_PACKAGE_NAME, SIMPLE_ACTIVITY, // package/activity to start 411 REQUEST_KEY_ACTION, REQUEST_VALUE_LISTENER_ADD_REMOVE); // action to perform 412 413 // Wait for timeout callback to ensure the broadcast was only sent once for the remaining 414 // listener. If we get a complete result this means that the removed listener was triggered. 415 assertEquals(RESULT_TIMEOUT, receiver.waitForActivity()); 416 receiver.close(); 417 418 // Confirm that the app confirmed that it successfully received a callback on the not 419 // removed listener, and did not receive one on the removed listener. 420 assertEquals(1, receiver.mIntents.size()); 421 422 Bundle extras = receiver.mIntents.get(0).getExtras(); 423 assertNotNull(extras); 424 425 int status = extras.getInt(REPLY_EXTRA_STATUS_KEY, REPLY_STATUS_NONE); 426 assertEquals(REPLY_EXTRA_SUCCESS_VALUE, status); 427 } 428 clearHistoricalStartInfo()429 private void clearHistoricalStartInfo() throws Exception { 430 executeShellCmd("am clear-start-info --user all " + STUB_PACKAGE_NAME); 431 } 432 433 /** Query the app start info object until it indicates the startup is complete. */ waitForAppStart()434 private ApplicationStartInfo waitForAppStart() { 435 List<ApplicationStartInfo> list; 436 437 for (int i = 0; i < MAX_WAITS_FOR_START; i++) { 438 list = ShellIdentityUtils.invokeMethodWithShellPermissions( 439 STUB_PACKAGE_NAME, 1, 440 mActivityManager::getExternalHistoricalProcessStartReasons, 441 android.Manifest.permission.DUMP); 442 443 if (list != null && list.size() == 1 444 && list.get(0).getStartupState() 445 == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN) { 446 return list.get(0); 447 } 448 sleep(WAIT_FOR_START_MS); 449 } 450 451 fail("The app didn't finish starting in time."); 452 return null; 453 } 454 sleep(long timeout)455 private void sleep(long timeout) { 456 try { 457 Thread.sleep(timeout); 458 } catch (InterruptedException e) { 459 } 460 } 461 462 @FormatMethod executeShellCmd(String cmdFormat, Object... args)463 private String executeShellCmd(String cmdFormat, Object... args) throws Exception { 464 String cmd = String.format(cmdFormat, args); 465 String result = SystemUtil.runShellCommand(mInstrumentation, cmd); 466 Log.d(TAG, String.format("Output for '%s': %s", cmd, result)); 467 return result; 468 } 469 verifyIds(ApplicationStartInfo info, int pid, int realUid, int packageUid, int definingUid)470 private void verifyIds(ApplicationStartInfo info, 471 int pid, int realUid, int packageUid, int definingUid) { 472 assertNotNull(info); 473 474 assertEquals(pid, info.getPid()); 475 assertEquals(realUid, info.getRealUid()); 476 assertEquals(definingUid, info.getDefiningUid()); 477 assertEquals(packageUid, info.getPackageUid()); 478 } 479 480 /** 481 * Verify that the info matches the passed state. 482 * Null arguments are skipped in verification. 483 */ verify(ApplicationStartInfo info, String packageName, String processName, Intent intent, int reason, int startType, int launchMode, int startupState)484 private void verify(ApplicationStartInfo info, 485 String packageName, String processName, Intent intent, 486 int reason, int startType, int launchMode, int startupState) { 487 assertNotNull(info); 488 489 if (packageName != null) { 490 assertTrue(packageName.equals(info.getPackageName())); 491 } 492 493 if (processName != null) { 494 assertTrue(processName.equals(info.getProcessName())); 495 } 496 497 if (intent != null) { 498 assertTrue(intent.filterEquals(info.getIntent())); 499 } 500 501 assertEquals(reason, info.getReason()); 502 assertEquals(startType, info.getStartType()); 503 assertEquals(launchMode, info.getLaunchMode()); 504 assertEquals(startupState, info.getStartupState()); 505 506 // Check that the appropriate timestamps exist based on the startup state 507 // and that they're in the right order. 508 Map<Integer, Long> timestamps = info.getStartupTimestamps(); 509 if (startupState == ApplicationStartInfo.STARTUP_STATE_STARTED) { 510 Long launchTimestamp = timestamps.get(ApplicationStartInfo.START_TIMESTAMP_LAUNCH); 511 assertTrue(launchTimestamp != null); 512 assertTrue(launchTimestamp > 0); 513 } 514 515 if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN) { 516 Long launchTimestamp = timestamps.get(ApplicationStartInfo.START_TIMESTAMP_LAUNCH); 517 assertTrue(launchTimestamp != null); 518 assertTrue(launchTimestamp > 0); 519 520 Long bindApplicationTimestamp = timestamps.get( 521 ApplicationStartInfo.START_TIMESTAMP_BIND_APPLICATION); 522 assertTrue(bindApplicationTimestamp != null); 523 assertTrue(bindApplicationTimestamp > 0); 524 525 assertTrue(launchTimestamp < bindApplicationTimestamp); 526 527 // TODO(287153617): Add support for START_TIMESTAMP_APPLICATION_ONCREATE 528 // and START_TIMESTAMP_FIRST_FRAME 529 } 530 } 531 532 private class ResultReceiverFilter extends BroadcastReceiver { 533 private String mActivityToFilter; 534 private int mResult = RESULT_TIMEOUT; 535 private int mResultsToWaitFor; 536 private static final int TIMEOUT_IN_MS = 5000; 537 List<Intent> mIntents = new ArrayList<Intent>(); 538 539 // Create the filter with the intent to look for. 540 ResultReceiverFilter(String activityToFilter, int resultsToWaitFor) { 541 mActivityToFilter = activityToFilter; 542 mResultsToWaitFor = resultsToWaitFor; 543 IntentFilter filter = new IntentFilter(); 544 filter.addAction(mActivityToFilter); 545 mInstrumentation.getTargetContext().registerReceiver(this, filter, 546 Context.RECEIVER_EXPORTED); 547 } 548 549 // Turn off the filter. 550 public void close() { 551 mInstrumentation.getTargetContext().unregisterReceiver(this); 552 } 553 554 @Override 555 public void onReceive(Context context, Intent intent) { 556 if (intent.getAction().equals(mActivityToFilter)) { 557 synchronized (this) { 558 mIntents.add(intent); 559 if (mIntents.size() >= mResultsToWaitFor) { 560 mResult = RESULT_PASS; 561 notifyAll(); 562 } 563 } 564 } 565 } 566 waitForActivity()567 public int waitForActivity() throws Exception { 568 AmUtils.waitForBroadcastBarrier(); 569 synchronized (this) { 570 try { 571 wait(TIMEOUT_IN_MS); 572 } catch (InterruptedException e) { 573 } 574 } 575 return mResult; 576 } 577 } 578 } 579