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