1 package org.robolectric.shadows; 2 3 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; 4 import static android.content.pm.PackageManager.PERMISSION_DENIED; 5 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 6 import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1; 7 import static android.os.Build.VERSION_CODES.M; 8 import static android.os.Build.VERSION_CODES.N; 9 import static android.os.Build.VERSION_CODES.N_MR1; 10 import static android.os.Build.VERSION_CODES.O; 11 import static android.os.Build.VERSION_CODES.P; 12 import static com.google.common.base.Preconditions.checkNotNull; 13 import static com.google.common.util.concurrent.Futures.immediateFuture; 14 import static com.google.common.util.concurrent.MoreExecutors.directExecutor; 15 import static org.robolectric.util.reflector.Reflector.reflector; 16 17 import android.annotation.Nullable; 18 import android.app.Activity; 19 import android.app.ActivityThread; 20 import android.app.Fragment; 21 import android.app.IUiAutomationConnection; 22 import android.app.Instrumentation; 23 import android.app.Instrumentation.ActivityResult; 24 import android.app.UiAutomation; 25 import android.content.ActivityNotFoundException; 26 import android.content.BroadcastReceiver; 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.ContextWrapper; 30 import android.content.Intent; 31 import android.content.Intent.FilterComparison; 32 import android.content.IntentFilter; 33 import android.content.ServiceConnection; 34 import android.os.Bundle; 35 import android.os.Handler; 36 import android.os.IBinder; 37 import android.os.Looper; 38 import android.os.Process; 39 import android.os.UserHandle; 40 import android.text.TextUtils; 41 import android.util.Pair; 42 import com.google.common.collect.ImmutableList; 43 import com.google.common.util.concurrent.AsyncFunction; 44 import com.google.common.util.concurrent.Futures; 45 import com.google.common.util.concurrent.ListenableFuture; 46 import java.util.ArrayList; 47 import java.util.Collections; 48 import java.util.Comparator; 49 import java.util.HashMap; 50 import java.util.HashSet; 51 import java.util.Iterator; 52 import java.util.LinkedHashMap; 53 import java.util.List; 54 import java.util.Map; 55 import java.util.Objects; 56 import java.util.Set; 57 import java.util.concurrent.ExecutionException; 58 import java.util.concurrent.Executor; 59 import java.util.concurrent.atomic.AtomicBoolean; 60 import javax.annotation.concurrent.GuardedBy; 61 import org.robolectric.RuntimeEnvironment; 62 import org.robolectric.annotation.Implementation; 63 import org.robolectric.annotation.Implements; 64 import org.robolectric.annotation.LooperMode; 65 import org.robolectric.annotation.RealObject; 66 import org.robolectric.shadow.api.Shadow; 67 import org.robolectric.shadows.ShadowActivity.IntentForResult; 68 import org.robolectric.shadows.ShadowApplication.Wrapper; 69 import org.robolectric.util.Logger; 70 import org.robolectric.util.ReflectionHelpers; 71 import org.robolectric.util.ReflectionHelpers.ClassParameter; 72 import org.robolectric.util.reflector.Direct; 73 import org.robolectric.util.reflector.ForType; 74 import org.robolectric.util.reflector.WithType; 75 76 @Implements(value = Instrumentation.class) 77 public class ShadowInstrumentation { 78 79 @RealObject private Instrumentation realObject; 80 81 private final List<Intent> startedActivities = Collections.synchronizedList(new ArrayList<>()); 82 private final List<IntentForResult> startedActivitiesForResults = 83 Collections.synchronizedList(new ArrayList<>()); 84 private final Map<FilterComparison, TargetAndRequestCode> intentRequestCodeMap = 85 Collections.synchronizedMap(new HashMap<>()); 86 private final List<Intent.FilterComparison> startedServices = 87 Collections.synchronizedList(new ArrayList<>()); 88 private final List<Intent.FilterComparison> stoppedServices = 89 Collections.synchronizedList(new ArrayList<>()); 90 private final List<Intent> broadcastIntents = Collections.synchronizedList(new ArrayList<>()); 91 private final Map<Intent, Bundle> broadcastOptions = Collections.synchronizedMap(new HashMap<>()); 92 private final Map<UserHandle, List<Intent>> broadcastIntentsForUser = 93 Collections.synchronizedMap(new HashMap<>()); 94 private final List<ServiceConnection> boundServiceConnections = 95 Collections.synchronizedList(new ArrayList<>()); 96 private final List<ServiceConnection> unboundServiceConnections = 97 Collections.synchronizedList(new ArrayList<>()); 98 99 @GuardedBy("itself") 100 private final List<Wrapper> registeredReceivers = new ArrayList<>(); 101 102 // map of pid+uid to granted permissions 103 private final Map<Pair<Integer, Integer>, Set<String>> grantedPermissionsMap = 104 Collections.synchronizedMap(new HashMap<>()); 105 private boolean unbindServiceShouldThrowIllegalArgument = false; 106 private SecurityException exceptionForBindService = null; 107 private boolean bindServiceCallsOnServiceConnectedInline; 108 private final Map<Intent.FilterComparison, ServiceConnectionDataWrapper> 109 serviceConnectionDataForIntent = Collections.synchronizedMap(new HashMap<>()); 110 // default values for bindService 111 private ServiceConnectionDataWrapper defaultServiceConnectionData = 112 new ServiceConnectionDataWrapper(null, null); 113 private final List<String> unbindableActions = Collections.synchronizedList(new ArrayList<>()); 114 private final List<ComponentName> unbindableComponents = 115 Collections.synchronizedList(new ArrayList<>()); 116 private final Map<String, Intent> stickyIntents = 117 Collections.synchronizedMap(new LinkedHashMap<>()); 118 private Handler mainHandler; 119 private final Map<ServiceConnection, ServiceConnectionDataWrapper> 120 serviceConnectionDataForServiceConnection = Collections.synchronizedMap(new HashMap<>()); 121 122 private boolean checkActivities; 123 // This will default to False in the future to correctly mirror real Android behavior. 124 private boolean unbindServiceCallsOnServiceDisconnected = true; 125 @Nullable private UiAutomation uiAutomation; 126 127 @Implementation(minSdk = P) startActivitySync(Intent intent, Bundle options)128 protected Activity startActivitySync(Intent intent, Bundle options) { 129 throw new UnsupportedOperationException("Implement me!!"); 130 } 131 132 @Implementation execStartActivities( Context who, IBinder contextThread, IBinder token, Activity target, Intent[] intents, Bundle options)133 protected void execStartActivities( 134 Context who, 135 IBinder contextThread, 136 IBinder token, 137 Activity target, 138 Intent[] intents, 139 Bundle options) { 140 for (Intent intent : intents) { 141 execStartActivity(who, contextThread, token, target, intent, -1, options); 142 } 143 } 144 145 @Implementation execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options)146 protected ActivityResult execStartActivity( 147 Context who, 148 IBinder contextThread, 149 IBinder token, 150 Activity target, 151 Intent intent, 152 int requestCode, 153 Bundle options) { 154 155 verifyActivityInManifest(intent); 156 logStartedActivity(intent, null, requestCode, options); 157 158 if (who == null) { 159 return null; 160 } 161 return reflector(_Instrumentation_.class, realObject) 162 .execStartActivity(who, contextThread, token, target, intent, requestCode, options); 163 } 164 165 @Implementation(maxSdk = LOLLIPOP_MR1) execStartActivity( Context who, IBinder contextThread, IBinder token, Fragment target, Intent intent, int requestCode, Bundle options)166 protected ActivityResult execStartActivity( 167 Context who, 168 IBinder contextThread, 169 IBinder token, 170 Fragment target, 171 Intent intent, 172 int requestCode, 173 Bundle options) { 174 verifyActivityInManifest(intent); 175 logStartedActivity(intent, null, requestCode, options); 176 return null; 177 } 178 179 @Implementation(minSdk = M) execStartActivity( Context who, IBinder contextThread, IBinder token, String target, Intent intent, int requestCode, Bundle options)180 protected ActivityResult execStartActivity( 181 Context who, 182 IBinder contextThread, 183 IBinder token, 184 String target, 185 Intent intent, 186 int requestCode, 187 Bundle options) { 188 verifyActivityInManifest(intent); 189 logStartedActivity(intent, target, requestCode, options); 190 191 return reflector(_Instrumentation_.class, realObject) 192 .execStartActivity(who, contextThread, token, target, intent, requestCode, options); 193 } 194 195 /** 196 * Behaves as {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle). 197 * 198 * <p>Currently ignores the user. 199 */ 200 @Implementation(maxSdk = N_MR1) execStartActivity( Context who, IBinder contextThread, IBinder token, Activity resultWho, Intent intent, int requestCode, Bundle options, UserHandle user)201 protected ActivityResult execStartActivity( 202 Context who, 203 IBinder contextThread, 204 IBinder token, 205 Activity resultWho, 206 Intent intent, 207 int requestCode, 208 Bundle options, 209 UserHandle user) { 210 return execStartActivity(who, contextThread, token, resultWho, intent, requestCode, options); 211 } 212 213 /** 214 * Behaves as {@link #execStartActivity(Context, IBinder, IBinder, String, Intent, int, Bundle). 215 * 216 * <p>Currently ignores the user. 217 */ 218 @Implementation(minSdk = O) execStartActivity( Context who, IBinder contextThread, IBinder token, String resultWho, Intent intent, int requestCode, Bundle options, UserHandle user)219 protected ActivityResult execStartActivity( 220 Context who, 221 IBinder contextThread, 222 IBinder token, 223 String resultWho, 224 Intent intent, 225 int requestCode, 226 Bundle options, 227 UserHandle user) { 228 return execStartActivity(who, contextThread, token, resultWho, intent, requestCode, options); 229 } 230 231 @Implementation setInTouchMode(boolean inTouchMode)232 protected void setInTouchMode(boolean inTouchMode) { 233 ShadowWindowManagerGlobal.setInTouchMode(inTouchMode); 234 } 235 236 @Implementation(maxSdk = M) getUiAutomation()237 protected UiAutomation getUiAutomation() { 238 return getUiAutomation(0); 239 } 240 241 @Implementation(minSdk = N) getUiAutomation(int flags)242 protected UiAutomation getUiAutomation(int flags) { 243 if (uiAutomation == null) { 244 // Create a new automation using reflection, the real code just connects through the 245 // automation connection and to the accessibility service, neither of which exist in 246 // Robolectric. 247 uiAutomation = 248 ReflectionHelpers.callConstructor( 249 UiAutomation.class, 250 ClassParameter.from(Looper.class, Looper.getMainLooper()), 251 ClassParameter.from( 252 IUiAutomationConnection.class, 253 ReflectionHelpers.createNullProxy(IUiAutomationConnection.class))); 254 } 255 return uiAutomation; 256 } 257 logStartedActivity(Intent intent, String target, int requestCode, Bundle options)258 private void logStartedActivity(Intent intent, String target, int requestCode, Bundle options) { 259 startedActivities.add(intent); 260 intentRequestCodeMap.put( 261 new FilterComparison(intent), new TargetAndRequestCode(target, requestCode)); 262 startedActivitiesForResults.add(new IntentForResult(intent, requestCode, options)); 263 } 264 verifyActivityInManifest(Intent intent)265 private void verifyActivityInManifest(Intent intent) { 266 if (checkActivities 267 && RuntimeEnvironment.getApplication() 268 .getPackageManager() 269 .resolveActivity(intent, MATCH_DEFAULT_ONLY) 270 == null) { 271 throw new ActivityNotFoundException(intent.getAction()); 272 } 273 } 274 sendOrderedBroadcastAsUser( Intent intent, UserHandle userHandle, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras, Context context)275 void sendOrderedBroadcastAsUser( 276 Intent intent, 277 UserHandle userHandle, 278 String receiverPermission, 279 BroadcastReceiver resultReceiver, 280 Handler scheduler, 281 int initialCode, 282 String initialData, 283 Bundle initialExtras, 284 Context context) { 285 List<Wrapper> receivers = 286 getAppropriateWrappers( 287 context, userHandle, intent, receiverPermission, /* broadcastOptions= */ null); 288 sortByPriority(receivers); 289 if (resultReceiver != null) { 290 receivers.add(new Wrapper(resultReceiver, null, context, null, scheduler, 0)); 291 } 292 postOrderedToWrappers(receivers, intent, initialCode, initialData, initialExtras, context); 293 } 294 assertNoBroadcastListenersOfActionRegistered(ContextWrapper context, String action)295 void assertNoBroadcastListenersOfActionRegistered(ContextWrapper context, String action) { 296 synchronized (registeredReceivers) { 297 for (Wrapper registeredReceiver : registeredReceivers) { 298 if (registeredReceiver.context == context.getBaseContext()) { 299 Iterator<String> actions = registeredReceiver.intentFilter.actionsIterator(); 300 while (actions.hasNext()) { 301 if (actions.next().equals(action)) { 302 RuntimeException e = 303 new IllegalStateException( 304 "Unexpected BroadcastReceiver on " 305 + context 306 + " with action " 307 + action 308 + " " 309 + registeredReceiver.broadcastReceiver 310 + " that was originally registered here:"); 311 e.setStackTrace(registeredReceiver.exception.getStackTrace()); 312 throw e; 313 } 314 } 315 } 316 } 317 } 318 } 319 320 /** Returns the BroadcaseReceivers wrappers, matching intent's action and permissions. */ getAppropriateWrappers( Context context, @Nullable UserHandle userHandle, Intent intent, String receiverPermission, @Nullable Bundle broadcastOptions)321 private List<Wrapper> getAppropriateWrappers( 322 Context context, 323 @Nullable UserHandle userHandle, 324 Intent intent, 325 String receiverPermission, 326 @Nullable Bundle broadcastOptions) { 327 broadcastIntents.add(intent); 328 this.broadcastOptions.put(intent, broadcastOptions); 329 330 if (userHandle != null) { 331 List<Intent> intentsForUser = broadcastIntentsForUser.get(userHandle); 332 if (intentsForUser == null) { 333 intentsForUser = new ArrayList<>(); 334 broadcastIntentsForUser.put(userHandle, intentsForUser); 335 } 336 intentsForUser.add(intent); 337 } 338 339 List<Wrapper> result = new ArrayList<>(); 340 341 List<Wrapper> copy = new ArrayList<>(); 342 synchronized (registeredReceivers) { 343 copy.addAll(registeredReceivers); 344 } 345 346 for (Wrapper wrapper : copy) { 347 if (broadcastReceiverMatchesIntent(context, wrapper, intent, receiverPermission)) { 348 result.add(wrapper); 349 } 350 } 351 System.err.format("Intent = %s; Matching wrappers: %s\n", intent, result); 352 return result; 353 } 354 broadcastReceiverMatchesIntent( Context broadcastContext, Wrapper wrapper, Intent intent, String receiverPermission)355 private static boolean broadcastReceiverMatchesIntent( 356 Context broadcastContext, Wrapper wrapper, Intent intent, String receiverPermission) { 357 String intentClass = 358 intent.getComponent() != null ? intent.getComponent().getClassName() : null; 359 boolean matchesIntentClass = 360 intentClass != null && intentClass.equals(wrapper.broadcastReceiver.getClass().getName()); 361 362 // The receiver must hold the permission specified by sendBroadcast, and the broadcaster must 363 // hold the permission specified by registerReceiver. 364 boolean hasPermissionFromManifest = 365 hasRequiredPermission(wrapper.context, receiverPermission) 366 && hasRequiredPermission(broadcastContext, wrapper.broadcastPermission); 367 // Many existing tests don't declare manifest permissions, relying on the old equality check. 368 boolean hasPermissionForBackwardsCompatibility = 369 TextUtils.equals(receiverPermission, wrapper.broadcastPermission); 370 boolean hasPermission = hasPermissionFromManifest || hasPermissionForBackwardsCompatibility; 371 372 boolean matchesAction = wrapper.intentFilter.matchAction(intent.getAction()); 373 374 final int match = 375 wrapper.intentFilter.matchData(intent.getType(), intent.getScheme(), intent.getData()); 376 boolean matchesDataAndType = 377 match != IntentFilter.NO_MATCH_DATA && match != IntentFilter.NO_MATCH_TYPE; 378 379 return matchesIntentClass || (hasPermission && matchesAction && matchesDataAndType); 380 } 381 382 /** A null {@code requiredPermission} indicates that no permission is required. */ hasRequiredPermission(Context context, @Nullable String requiredPermission)383 static boolean hasRequiredPermission(Context context, @Nullable String requiredPermission) { 384 if (requiredPermission == null) { 385 return true; 386 } 387 // Check manifest-based permissions from PackageManager. 388 Context applicationContext = RuntimeEnvironment.getApplication(); 389 if (applicationContext 390 .getPackageManager() 391 .checkPermission(requiredPermission, context.getPackageName()) 392 == PERMISSION_GRANTED) { 393 return true; 394 } 395 // Check dynamically-granted permissions from here in ShadowInstrumentation. 396 if (Objects.equals(context.getPackageName(), applicationContext.getPackageName()) 397 && applicationContext.checkPermission(requiredPermission, Process.myPid(), Process.myUid()) 398 == PERMISSION_GRANTED) { 399 return true; 400 } 401 return false; 402 } 403 postIntent( Intent intent, Wrapper wrapper, final AtomicBoolean abort, Context context, int resultCode)404 private void postIntent( 405 Intent intent, Wrapper wrapper, final AtomicBoolean abort, Context context, int resultCode) { 406 final Handler scheduler = 407 (wrapper.scheduler != null) ? wrapper.scheduler : getMainHandler(context); 408 final BroadcastReceiver receiver = wrapper.broadcastReceiver; 409 final ShadowBroadcastReceiver shReceiver = Shadow.extract(receiver); 410 final Intent broadcastIntent = intent; 411 scheduler.post( 412 new Runnable() { 413 @Override 414 public void run() { 415 receiver.setPendingResult( 416 ShadowBroadcastPendingResult.create(resultCode, null, null, false)); 417 shReceiver.onReceive(context, broadcastIntent, abort); 418 } 419 }); 420 } 421 postToWrappers( List<Wrapper> wrappers, Intent intent, Context context, int resultCode)422 private void postToWrappers( 423 List<Wrapper> wrappers, Intent intent, Context context, int resultCode) { 424 AtomicBoolean abort = 425 new AtomicBoolean(false); // abort state is shared among all broadcast receivers 426 for (Wrapper wrapper : wrappers) { 427 postIntent(intent, wrapper, abort, context, resultCode); 428 } 429 } 430 postOrderedToWrappers( List<Wrapper> wrappers, final Intent intent, int initialCode, String data, Bundle extras, final Context context)431 private void postOrderedToWrappers( 432 List<Wrapper> wrappers, 433 final Intent intent, 434 int initialCode, 435 String data, 436 Bundle extras, 437 final Context context) { 438 final AtomicBoolean abort = 439 new AtomicBoolean(false); // abort state is shared among all broadcast receivers 440 ListenableFuture<BroadcastResultHolder> future = 441 immediateFuture(new BroadcastResultHolder(initialCode, data, extras)); 442 for (final Wrapper wrapper : wrappers) { 443 future = postIntent(wrapper, intent, future, abort, context); 444 } 445 final ListenableFuture<?> finalFuture = future; 446 future.addListener( 447 new Runnable() { 448 @Override 449 public void run() { 450 getMainHandler(context) 451 .post( 452 new Runnable() { 453 @Override 454 public void run() { 455 try { 456 finalFuture.get(); 457 } catch (InterruptedException | ExecutionException e) { 458 throw new RuntimeException(e); 459 } 460 } 461 }); 462 } 463 }, 464 directExecutor()); 465 } 466 467 /** 468 * Enforces that BroadcastReceivers invoked during an ordered broadcast run serially, passing 469 * along their results. 470 */ postIntent( final Wrapper wrapper, final Intent intent, ListenableFuture<BroadcastResultHolder> oldResult, final AtomicBoolean abort, final Context context)471 private ListenableFuture<BroadcastResultHolder> postIntent( 472 final Wrapper wrapper, 473 final Intent intent, 474 ListenableFuture<BroadcastResultHolder> oldResult, 475 final AtomicBoolean abort, 476 final Context context) { 477 final Handler scheduler = 478 (wrapper.scheduler != null) ? wrapper.scheduler : getMainHandler(context); 479 return Futures.transformAsync( 480 oldResult, 481 new AsyncFunction<BroadcastResultHolder, BroadcastResultHolder>() { 482 @Override 483 public ListenableFuture<BroadcastResultHolder> apply( 484 BroadcastResultHolder broadcastResultHolder) throws Exception { 485 final BroadcastReceiver.PendingResult result = 486 ShadowBroadcastPendingResult.create( 487 broadcastResultHolder.resultCode, 488 broadcastResultHolder.resultData, 489 broadcastResultHolder.resultExtras, 490 true /*ordered */); 491 wrapper.broadcastReceiver.setPendingResult(result); 492 scheduler.post( 493 () -> { 494 ShadowBroadcastReceiver shadowBroadcastReceiver = 495 Shadow.extract(wrapper.broadcastReceiver); 496 shadowBroadcastReceiver.onReceive(context, intent, abort); 497 }); 498 return BroadcastResultHolder.transform(result); 499 } 500 }, 501 directExecutor()); 502 } 503 504 /** 505 * Broadcasts the {@code Intent} by iterating through the registered receivers, invoking their 506 * filters including permissions, and calling {@code onReceive(Application, Intent)} as 507 * appropriate. Does not enqueue the {@code Intent} for later inspection. 508 * 509 * @param context 510 * @param intent the {@code Intent} to broadcast todo: enqueue the Intent for later inspection 511 */ 512 void sendBroadcastWithPermission( 513 Intent intent, UserHandle userHandle, String receiverPermission, Context context) { 514 sendBroadcastWithPermission( 515 intent, 516 userHandle, 517 receiverPermission, 518 context, 519 /* broadcastOptions= */ null, 520 /* resultCode= */ 0); 521 } 522 523 void sendBroadcastWithPermission( 524 Intent intent, String receiverPermission, Context context, int resultCode) { 525 sendBroadcastWithPermission( 526 intent, /* userHandle= */ null, receiverPermission, context, null, resultCode); 527 } 528 529 void sendBroadcastWithPermission( 530 Intent intent, 531 String receiverPermission, 532 Context context, 533 @Nullable Bundle broadcastOptions, 534 int resultCode) { 535 sendBroadcastWithPermission( 536 intent, /* userHandle= */ null, receiverPermission, context, broadcastOptions, resultCode); 537 } 538 539 void sendBroadcastWithPermission( 540 Intent intent, 541 UserHandle userHandle, 542 String receiverPermission, 543 Context context, 544 @Nullable Bundle broadcastOptions, 545 int resultCode) { 546 List<Wrapper> wrappers = 547 getAppropriateWrappers(context, userHandle, intent, receiverPermission, broadcastOptions); 548 postToWrappers(wrappers, intent, context, resultCode); 549 } 550 551 void sendOrderedBroadcastWithPermission( 552 Intent intent, String receiverPermission, Context context) { 553 List<Wrapper> wrappers = 554 getAppropriateWrappers( 555 context, 556 /* userHandle= */ null, 557 intent, 558 receiverPermission, 559 /* broadcastOptions= */ null); 560 // sort by the decrease of priorities 561 sortByPriority(wrappers); 562 563 postOrderedToWrappers(wrappers, intent, 0, null, null, context); 564 } 565 566 private void sortByPriority(List<Wrapper> wrappers) { 567 Collections.sort( 568 wrappers, 569 new Comparator<Wrapper>() { 570 @Override 571 public int compare(Wrapper o1, Wrapper o2) { 572 return Integer.compare( 573 o2.getIntentFilter().getPriority(), o1.getIntentFilter().getPriority()); 574 } 575 }); 576 } 577 578 List<Intent> getBroadcastIntents() { 579 return broadcastIntents; 580 } 581 582 @Nullable 583 Bundle getBroadcastOptions(Intent intent) { 584 synchronized (broadcastOptions) { 585 for (Intent broadcastIntent : broadcastOptions.keySet()) { 586 if (broadcastIntent.filterEquals(intent)) { 587 return broadcastOptions.get(broadcastIntent); 588 } 589 } 590 return null; 591 } 592 } 593 594 List<Intent> getBroadcastIntentsForUser(UserHandle userHandle) { 595 List<Intent> intentsForUser = broadcastIntentsForUser.get(userHandle); 596 if (intentsForUser == null) { 597 intentsForUser = new ArrayList<>(); 598 broadcastIntentsForUser.put(userHandle, intentsForUser); 599 } 600 return intentsForUser; 601 } 602 603 void clearBroadcastIntents() { 604 broadcastIntents.clear(); 605 broadcastOptions.clear(); 606 broadcastIntentsForUser.clear(); 607 } 608 609 Intent getNextStartedActivity() { 610 if (startedActivities.isEmpty()) { 611 return null; 612 } else { 613 return startedActivities.remove(startedActivities.size() - 1); 614 } 615 } 616 617 Intent peekNextStartedActivity() { 618 if (startedActivities.isEmpty()) { 619 return null; 620 } else { 621 return startedActivities.get(startedActivities.size() - 1); 622 } 623 } 624 625 /** 626 * Clears all {@code Intent}s started by {@link #execStartActivity(Context, IBinder, IBinder, 627 * Activity, Intent, int, Bundle)}, {@link #execStartActivity(Context, IBinder, IBinder, Fragment, 628 * Intent, int, Bundle)}, and {@link #execStartActivity(Context, IBinder, IBinder, String, Intent, 629 * int, Bundle)}. 630 */ 631 void clearNextStartedActivities() { 632 startedActivities.clear(); 633 startedActivitiesForResults.clear(); 634 } 635 636 IntentForResult getNextStartedActivityForResult() { 637 if (startedActivitiesForResults.isEmpty()) { 638 return null; 639 } else { 640 return startedActivitiesForResults.remove(startedActivitiesForResults.size() - 1); 641 } 642 } 643 644 IntentForResult peekNextStartedActivityForResult() { 645 if (startedActivitiesForResults.isEmpty()) { 646 return null; 647 } else { 648 return startedActivitiesForResults.get(startedActivitiesForResults.size() - 1); 649 } 650 } 651 652 void checkActivities(boolean checkActivities) { 653 this.checkActivities = checkActivities; 654 } 655 656 TargetAndRequestCode getTargetAndRequestCodeForIntent(Intent requestIntent) { 657 return checkNotNull( 658 intentRequestCodeMap.get(new Intent.FilterComparison(requestIntent)), 659 "No intent matches %s among %s", 660 requestIntent, 661 intentRequestCodeMap.keySet()); 662 } 663 664 protected ComponentName startService(Intent intent) { 665 startedServices.add(new Intent.FilterComparison(intent)); 666 if (intent.getComponent() != null) { 667 return intent.getComponent(); 668 } 669 return new ComponentName("some.service.package", "SomeServiceName-FIXME"); 670 } 671 672 boolean stopService(Intent name) { 673 stoppedServices.add(new Intent.FilterComparison(name)); 674 return startedServices.contains(new Intent.FilterComparison(name)); 675 } 676 677 /** 678 * Set the default IBinder implementation that will be returned when the service is bound using 679 * the specified Intent. The IBinder can implement the methods to simulate a bound Service. Useful 680 * for testing the ServiceConnection implementation. 681 * 682 * @param name The ComponentName of the Service 683 * @param service The IBinder implementation to return when the service is bound. 684 */ 685 void setComponentNameAndServiceForBindService(ComponentName name, IBinder service) { 686 defaultServiceConnectionData = new ServiceConnectionDataWrapper(name, service); 687 } 688 689 /** 690 * Set the IBinder implementation that will be returned when the service is bound using the 691 * specified Intent. The IBinder can implement the methods to simulate a bound Service. Useful for 692 * testing the ServiceConnection implementation. 693 * 694 * @param intent The exact Intent used in Context#bindService(...) 695 * @param name The ComponentName of the Service 696 * @param service The IBinder implementation to return when the service is bound. 697 */ 698 void setComponentNameAndServiceForBindServiceForIntent( 699 Intent intent, ComponentName name, IBinder service) { 700 serviceConnectionDataForIntent.put( 701 new Intent.FilterComparison(intent), new ServiceConnectionDataWrapper(name, service)); 702 } 703 704 protected boolean bindService( 705 Intent intent, int flags, Executor executor, ServiceConnection serviceConnection) { 706 return bindService(intent, serviceConnection, new ExecutorServiceCallbackScheduler(executor)); 707 } 708 709 protected boolean bindService( 710 final Intent intent, final ServiceConnection serviceConnection, int flags) { 711 return bindService(intent, serviceConnection, new HandlerCallbackScheduler()); 712 } 713 714 private boolean bindService( 715 final Intent intent, 716 final ServiceConnection serviceConnection, 717 ServiceCallbackScheduler serviceCallbackScheduler) { 718 boundServiceConnections.add(serviceConnection); 719 unboundServiceConnections.remove(serviceConnection); 720 if (exceptionForBindService != null) { 721 throw exceptionForBindService; 722 } 723 final Intent.FilterComparison filterComparison = new Intent.FilterComparison(intent); 724 final ServiceConnectionDataWrapper serviceConnectionDataWrapper = 725 serviceConnectionDataForIntent.getOrDefault(filterComparison, defaultServiceConnectionData); 726 if (unbindableActions.contains(intent.getAction()) 727 || unbindableComponents.contains(intent.getComponent()) 728 || unbindableComponents.contains( 729 serviceConnectionDataWrapper.componentNameForBindService)) { 730 return false; 731 } 732 startedServices.add(filterComparison); 733 Runnable onServiceConnectedRunnable = 734 () -> { 735 serviceConnectionDataForServiceConnection.put( 736 serviceConnection, serviceConnectionDataWrapper); 737 serviceConnection.onServiceConnected( 738 serviceConnectionDataWrapper.componentNameForBindService, 739 serviceConnectionDataWrapper.binderForBindService); 740 }; 741 742 if (bindServiceCallsOnServiceConnectedInline) { 743 onServiceConnectedRunnable.run(); 744 } else { 745 serviceCallbackScheduler.schedule(onServiceConnectedRunnable); 746 } 747 return true; 748 } 749 750 protected void setUnbindServiceCallsOnServiceDisconnected(boolean flag) { 751 unbindServiceCallsOnServiceDisconnected = flag; 752 } 753 754 protected void unbindService(final ServiceConnection serviceConnection) { 755 if (unbindServiceShouldThrowIllegalArgument) { 756 throw new IllegalArgumentException(); 757 } 758 759 unboundServiceConnections.add(serviceConnection); 760 boundServiceConnections.remove(serviceConnection); 761 Handler handler = new Handler(Looper.getMainLooper()); 762 handler.post( 763 () -> { 764 final ServiceConnectionDataWrapper serviceConnectionDataWrapper; 765 if (serviceConnectionDataForServiceConnection.containsKey(serviceConnection)) { 766 serviceConnectionDataWrapper = 767 serviceConnectionDataForServiceConnection.get(serviceConnection); 768 } else { 769 serviceConnectionDataWrapper = defaultServiceConnectionData; 770 } 771 if (unbindServiceCallsOnServiceDisconnected) { 772 Logger.warn( 773 "Configured to call onServiceDisconnected when unbindService is called. This is" 774 + " not accurate Android behavior. Please update your tests and call" 775 + " ShadowApplication#setUnbindServiceCallsOnServiceDisconnected(false). This" 776 + " will become default behavior in the future, which may break your tests if" 777 + " you are expecting this inaccurate behavior."); 778 serviceConnection.onServiceDisconnected( 779 serviceConnectionDataWrapper.componentNameForBindService); 780 } 781 }); 782 } 783 784 protected List<ServiceConnection> getBoundServiceConnections() { 785 return boundServiceConnections; 786 } 787 788 void setUnbindServiceShouldThrowIllegalArgument(boolean flag) { 789 unbindServiceShouldThrowIllegalArgument = flag; 790 } 791 792 void setThrowInBindService(SecurityException e) { 793 exceptionForBindService = e; 794 } 795 796 void setBindServiceCallsOnServiceConnectedDirectly( 797 boolean bindServiceCallsOnServiceConnectedInline) { 798 this.bindServiceCallsOnServiceConnectedInline = bindServiceCallsOnServiceConnectedInline; 799 } 800 801 protected List<ServiceConnection> getUnboundServiceConnections() { 802 return unboundServiceConnections; 803 } 804 805 void declareActionUnbindable(String action) { 806 unbindableActions.add(action); 807 } 808 809 void declareComponentUnbindable(ComponentName component) { 810 checkNotNull(component); 811 unbindableComponents.add(component); 812 } 813 814 public List<String> getUnbindableActions() { 815 return unbindableActions; 816 } 817 818 List<ComponentName> getUnbindableComponents() { 819 return unbindableComponents; 820 } 821 822 /** 823 * Consumes the most recent {@code Intent} started by {@link 824 * #startService(android.content.Intent)} and returns it. 825 * 826 * @return the most recently started {@code Intent} 827 */ 828 Intent getNextStartedService() { 829 if (startedServices.isEmpty()) { 830 return null; 831 } else { 832 return startedServices.remove(0).getIntent(); 833 } 834 } 835 836 /** 837 * Returns the most recent {@code Intent} started by {@link #startService(android.content.Intent)} 838 * without consuming it. 839 * 840 * @return the most recently started {@code Intent} 841 */ 842 Intent peekNextStartedService() { 843 if (startedServices.isEmpty()) { 844 return null; 845 } else { 846 return startedServices.get(0).getIntent(); 847 } 848 } 849 850 /** Clears all {@code Intent} started by {@link #startService(android.content.Intent)}. */ 851 void clearStartedServices() { 852 startedServices.clear(); 853 } 854 855 /** 856 * Returns all {@code Intent} started by {@link #startService(android.content.Intent)} without 857 * consuming them. 858 * 859 * @return the list of {@code Intent} 860 */ 861 List<Intent> getAllStartedServices() { 862 ArrayList<Intent> startedServicesIntents = new ArrayList<>(); 863 for (Intent.FilterComparison filterComparison : startedServices) { 864 startedServicesIntents.add(filterComparison.getIntent()); 865 } 866 return startedServicesIntents; 867 } 868 869 /** 870 * Consumes the {@code Intent} requested to stop a service by {@link 871 * #stopService(android.content.Intent)} from the bottom of the stack of stop requests. 872 */ 873 Intent getNextStoppedService() { 874 if (stoppedServices.isEmpty()) { 875 return null; 876 } else { 877 return stoppedServices.remove(0).getIntent(); 878 } 879 } 880 881 void sendStickyBroadcast(Intent intent, Context context) { 882 stickyIntents.put(intent.getAction(), intent); 883 sendBroadcast(intent, context); 884 } 885 886 void sendBroadcast(Intent intent, Context context) { 887 sendBroadcastWithPermission( 888 intent, /* userHandle= */ null, /* receiverPermission= */ null, context); 889 } 890 891 Intent registerReceiver( 892 BroadcastReceiver receiver, IntentFilter filter, int flags, Context context) { 893 return registerReceiver(receiver, filter, null, null, flags, context); 894 } 895 896 Intent registerReceiver( 897 BroadcastReceiver receiver, 898 IntentFilter filter, 899 String broadcastPermission, 900 Handler scheduler, 901 int flags, 902 Context context) { 903 return registerReceiverWithContext( 904 receiver, filter, broadcastPermission, scheduler, flags, context); 905 } 906 907 Intent registerReceiverWithContext( 908 BroadcastReceiver receiver, 909 IntentFilter filter, 910 String broadcastPermission, 911 Handler scheduler, 912 int flags, 913 Context context) { 914 if (receiver != null) { 915 synchronized (registeredReceivers) { 916 registeredReceivers.add( 917 new Wrapper(receiver, filter, context, broadcastPermission, scheduler, flags)); 918 } 919 } 920 return processStickyIntents(filter, receiver, context); 921 } 922 923 private Intent processStickyIntents( 924 IntentFilter filter, BroadcastReceiver receiver, Context context) { 925 Intent result = null; 926 for (Intent stickyIntent : stickyIntents.values()) { 927 if (filter.matchAction(stickyIntent.getAction())) { 928 if (result == null) { 929 result = stickyIntent; 930 } 931 if (receiver != null) { 932 receiver.setPendingResult(ShadowBroadcastPendingResult.createSticky(stickyIntent)); 933 receiver.onReceive(context, stickyIntent); 934 receiver.setPendingResult(null); 935 } else if (result != null) { 936 break; 937 } 938 } 939 } 940 return result; 941 } 942 943 void unregisterReceiver(BroadcastReceiver broadcastReceiver) { 944 boolean found = false; 945 946 synchronized (registeredReceivers) { 947 Iterator<Wrapper> iterator = registeredReceivers.iterator(); 948 while (iterator.hasNext()) { 949 Wrapper wrapper = iterator.next(); 950 if (wrapper.broadcastReceiver == broadcastReceiver) { 951 iterator.remove(); 952 found = true; 953 } 954 } 955 } 956 957 if (!found) { 958 throw new IllegalArgumentException("Receiver not registered: " + broadcastReceiver); 959 } 960 } 961 962 void clearRegisteredReceivers() { 963 synchronized (registeredReceivers) { 964 registeredReceivers.clear(); 965 } 966 } 967 968 /** 969 * @deprecated use PackageManager.queryBroadcastReceivers instead 970 */ 971 @Deprecated 972 boolean hasReceiverForIntent(Intent intent) { 973 synchronized (registeredReceivers) { 974 for (Wrapper wrapper : registeredReceivers) { 975 if (wrapper.intentFilter.matchAction(intent.getAction())) { 976 return true; 977 } 978 } 979 } 980 return false; 981 } 982 983 /** 984 * @deprecated use PackageManager.queryBroadcastReceivers instead 985 */ 986 @Deprecated 987 List<BroadcastReceiver> getReceiversForIntent(Intent intent) { 988 ArrayList<BroadcastReceiver> broadcastReceivers = new ArrayList<>(); 989 990 synchronized (registeredReceivers) { 991 for (Wrapper wrapper : registeredReceivers) { 992 if (wrapper.intentFilter.matchAction(intent.getAction())) { 993 broadcastReceivers.add(wrapper.getBroadcastReceiver()); 994 } 995 } 996 } 997 return broadcastReceivers; 998 } 999 1000 /** 1001 * @return copy of the list of {@link Wrapper}s for registered receivers 1002 */ 1003 ImmutableList<Wrapper> getRegisteredReceivers() { 1004 ImmutableList<Wrapper> copy; 1005 synchronized (registeredReceivers) { 1006 copy = ImmutableList.copyOf(registeredReceivers); 1007 } 1008 1009 return copy; 1010 } 1011 1012 int checkPermission(String permission, int pid, int uid) { 1013 if (pid == -1) { 1014 for (Map.Entry<Pair<Integer, Integer>, Set<String>> entry : 1015 grantedPermissionsMap.entrySet()) { 1016 if (entry.getKey().second == uid && entry.getValue().contains(permission)) { 1017 return PERMISSION_GRANTED; 1018 } 1019 } 1020 return PERMISSION_DENIED; 1021 } else { 1022 Set<String> grantedPermissionsForPidUid = grantedPermissionsMap.get(new Pair(pid, uid)); 1023 return grantedPermissionsForPidUid != null && grantedPermissionsForPidUid.contains(permission) 1024 ? PERMISSION_GRANTED 1025 : PERMISSION_DENIED; 1026 } 1027 } 1028 1029 void grantPermissions(String... permissionNames) { 1030 grantPermissions(Process.myPid(), Process.myUid(), permissionNames); 1031 } 1032 1033 void grantPermissions(int pid, int uid, String... permissions) { 1034 Set<String> grantedPermissionsForPidUid = grantedPermissionsMap.get(new Pair<>(pid, uid)); 1035 if (grantedPermissionsForPidUid == null) { 1036 grantedPermissionsForPidUid = new HashSet<>(); 1037 grantedPermissionsMap.put(new Pair<>(pid, uid), grantedPermissionsForPidUid); 1038 } 1039 Collections.addAll(grantedPermissionsForPidUid, permissions); 1040 } 1041 1042 void denyPermissions(String... permissionNames) { 1043 denyPermissions(Process.myPid(), Process.myUid(), permissionNames); 1044 } 1045 1046 void denyPermissions(int pid, int uid, String... permissions) { 1047 Set<String> grantedPermissionsForPidUid = grantedPermissionsMap.get(new Pair<>(pid, uid)); 1048 if (grantedPermissionsForPidUid != null) { 1049 for (String permissionName : permissions) { 1050 grantedPermissionsForPidUid.remove(permissionName); 1051 } 1052 } 1053 } 1054 1055 private Handler getMainHandler(Context context) { 1056 if (mainHandler == null) { 1057 mainHandler = new Handler(context.getMainLooper()); 1058 } 1059 return mainHandler; 1060 } 1061 1062 /** Reflector interface for {@link Instrumentation}'s internals. */ 1063 @ForType(Instrumentation.class) 1064 public interface _Instrumentation_ { 1065 void init( 1066 ActivityThread thread, 1067 Context instrContext, 1068 Context appContext, 1069 ComponentName component, 1070 @WithType("android.app.IInstrumentationWatcher") Object watcher, 1071 @WithType("android.app.IUiAutomationConnection") Object uiAutomationConnection); 1072 1073 @Direct 1074 ActivityResult execStartActivity( 1075 Context who, 1076 IBinder contextThread, 1077 IBinder token, 1078 Activity target, 1079 Intent intent, 1080 int requestCode, 1081 Bundle options); 1082 1083 @Direct 1084 ActivityResult execStartActivity( 1085 Context who, 1086 IBinder contextThread, 1087 IBinder token, 1088 String target, 1089 Intent intent, 1090 int requestCode, 1091 Bundle options); 1092 } 1093 1094 private static final class BroadcastResultHolder { 1095 private final int resultCode; 1096 private final String resultData; 1097 private final Bundle resultExtras; 1098 1099 private BroadcastResultHolder(int resultCode, String resultData, Bundle resultExtras) { 1100 this.resultCode = resultCode; 1101 this.resultData = resultData; 1102 this.resultExtras = resultExtras; 1103 } 1104 1105 private static ListenableFuture<BroadcastResultHolder> transform( 1106 BroadcastReceiver.PendingResult result) { 1107 ShadowBroadcastPendingResult shadowBroadcastPendingResult = Shadow.extract(result); 1108 return Futures.transform( 1109 shadowBroadcastPendingResult.getFuture(), 1110 pendingResult -> 1111 new BroadcastResultHolder( 1112 pendingResult.getResultCode(), 1113 pendingResult.getResultData(), 1114 pendingResult.getResultExtras(false)), 1115 directExecutor()); 1116 } 1117 } 1118 1119 private static class ServiceConnectionDataWrapper { 1120 public final ComponentName componentNameForBindService; 1121 public final IBinder binderForBindService; 1122 1123 private ServiceConnectionDataWrapper( 1124 ComponentName componentNameForBindService, IBinder binderForBindService) { 1125 this.componentNameForBindService = componentNameForBindService; 1126 this.binderForBindService = binderForBindService; 1127 } 1128 } 1129 1130 /** Handles thread on which service lifecycle callbacks are run. */ 1131 private interface ServiceCallbackScheduler { 1132 void schedule(Runnable runnable); 1133 } 1134 1135 private static final class ExecutorServiceCallbackScheduler implements ServiceCallbackScheduler { 1136 private final Executor executor; 1137 1138 ExecutorServiceCallbackScheduler(Executor executor) { 1139 this.executor = executor; 1140 } 1141 1142 @Override 1143 public void schedule(Runnable runnable) { 1144 executor.execute(runnable); 1145 } 1146 } 1147 1148 private static final class HandlerCallbackScheduler implements ServiceCallbackScheduler { 1149 private final Handler mainHandler = new Handler(Looper.getMainLooper()); 1150 1151 @Override 1152 public void schedule(Runnable runnable) { 1153 mainHandler.post(runnable); 1154 } 1155 } 1156 1157 static final class TargetAndRequestCode { 1158 final String target; 1159 final int requestCode; 1160 1161 private TargetAndRequestCode(String target, int requestCode) { 1162 this.target = target; 1163 this.requestCode = requestCode; 1164 } 1165 } 1166 1167 public static Instrumentation getInstrumentation() { 1168 ActivityThread activityThread = (ActivityThread) RuntimeEnvironment.getActivityThread(); 1169 if (activityThread != null) { 1170 return activityThread.getInstrumentation(); 1171 } 1172 return null; 1173 } 1174 1175 /** 1176 * Executes a runnable depending on the LooperMode. 1177 * 1178 * <p>For INSTRUMENTATION_TEST mode, will post the runnable to the instrumentation thread and 1179 * block the caller's thread until that runnable is executed. 1180 * 1181 * <p>For other modes, simply executes the runnable. 1182 * 1183 * @param runnable a runnable to be executed 1184 */ 1185 public static void runOnMainSyncNoIdle(Runnable runnable) { 1186 if (ShadowLooper.looperMode() == LooperMode.Mode.INSTRUMENTATION_TEST 1187 && Looper.myLooper() != Looper.getMainLooper()) { 1188 checkNotNull(getInstrumentation()).runOnMainSync(runnable); 1189 } else { 1190 runnable.run(); 1191 } 1192 } 1193 } 1194