1 package org.robolectric.shadows; 2 3 import static android.content.pm.PackageManager.PERMISSION_DENIED; 4 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 5 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; 6 import static android.os.Build.VERSION_CODES.LOLLIPOP; 7 import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1; 8 import static android.os.Build.VERSION_CODES.M; 9 import static android.os.Build.VERSION_CODES.P; 10 import static com.google.common.util.concurrent.Futures.immediateFuture; 11 import static com.google.common.util.concurrent.MoreExecutors.directExecutor; 12 import static org.robolectric.shadow.api.Shadow.directlyOn; 13 14 import android.app.Activity; 15 import android.app.ActivityThread; 16 import android.app.Fragment; 17 import android.app.Instrumentation; 18 import android.app.Instrumentation.ActivityResult; 19 import android.content.ActivityNotFoundException; 20 import android.content.BroadcastReceiver; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.ContextWrapper; 24 import android.content.Intent; 25 import android.content.Intent.FilterComparison; 26 import android.content.IntentFilter; 27 import android.content.ServiceConnection; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.os.IBinder; 31 import android.os.Looper; 32 import android.os.Process; 33 import android.os.UserHandle; 34 import android.util.Pair; 35 import com.google.common.util.concurrent.AsyncFunction; 36 import com.google.common.util.concurrent.Futures; 37 import com.google.common.util.concurrent.ListenableFuture; 38 import java.util.ArrayList; 39 import java.util.Collections; 40 import java.util.Comparator; 41 import java.util.HashMap; 42 import java.util.HashSet; 43 import java.util.Iterator; 44 import java.util.LinkedHashMap; 45 import java.util.List; 46 import java.util.Map; 47 import java.util.Set; 48 import java.util.concurrent.ExecutionException; 49 import java.util.concurrent.atomic.AtomicBoolean; 50 import org.robolectric.RuntimeEnvironment; 51 import org.robolectric.annotation.Implementation; 52 import org.robolectric.annotation.Implements; 53 import org.robolectric.annotation.RealObject; 54 import org.robolectric.shadow.api.Shadow; 55 import org.robolectric.shadows.ShadowActivity.IntentForResult; 56 import org.robolectric.shadows.ShadowApplication.Wrapper; 57 58 @Implements(value = Instrumentation.class, looseSignatures = true) 59 public class ShadowInstrumentation { 60 61 @RealObject private Instrumentation realObject; 62 63 private List<Intent> startedActivities = new ArrayList<>(); 64 private List<IntentForResult> startedActivitiesForResults = new ArrayList<>(); 65 private Map<FilterComparison, Integer> intentRequestCodeMap = new HashMap<>(); 66 private List<Intent.FilterComparison> startedServices = new ArrayList<>(); 67 private List<Intent.FilterComparison> stoppedServices = new ArrayList<>(); 68 private List<Intent> broadcastIntents = new ArrayList<>(); 69 private List<ServiceConnection> boundServiceConnections = new ArrayList<>(); 70 private List<ServiceConnection> unboundServiceConnections = new ArrayList<>(); 71 private List<Wrapper> registeredReceivers = new ArrayList<>(); 72 // map of pid+uid to granted permissions 73 private final Map<Pair<Integer, Integer>, Set<String>> grantedPermissionsMap = new HashMap<>(); 74 private boolean unbindServiceShouldThrowIllegalArgument = false; 75 private Map<Intent.FilterComparison, ServiceConnectionDataWrapper> 76 serviceConnectionDataForIntent = new HashMap<>(); 77 // default values for bindService 78 private ServiceConnectionDataWrapper defaultServiceConnectionData = 79 new ServiceConnectionDataWrapper(null, null); 80 private List<String> unbindableActions = new ArrayList<>(); 81 private Map<String, Intent> stickyIntents = new LinkedHashMap<>(); 82 private Handler mainHandler; 83 private Map<ServiceConnection, ServiceConnectionDataWrapper> 84 serviceConnectionDataForServiceConnection = new HashMap<>(); 85 86 private boolean checkActivities; 87 88 @Implementation(minSdk = P) startActivitySync(Intent intent, Bundle options)89 protected Activity startActivitySync(Intent intent, Bundle options) { 90 throw new UnsupportedOperationException("Implement me!!"); 91 } 92 93 @Implementation execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options)94 protected ActivityResult execStartActivity( 95 Context who, 96 IBinder contextThread, 97 IBinder token, 98 Activity target, 99 Intent intent, 100 int requestCode, 101 Bundle options) { 102 103 verifyActivityInManifest(intent); 104 logStartedActivity(intent, requestCode, options); 105 106 if (who == null) { 107 return null; 108 } 109 return directlyOn(realObject, Instrumentation.class) 110 .execStartActivity(who, contextThread, token, target, intent, requestCode, options); 111 } 112 113 @Implementation(maxSdk = LOLLIPOP_MR1) execStartActivity( Context who, IBinder contextThread, IBinder token, Fragment target, Intent intent, int requestCode, Bundle options)114 protected ActivityResult execStartActivity( 115 Context who, 116 IBinder contextThread, 117 IBinder token, 118 Fragment target, 119 Intent intent, 120 int requestCode, 121 Bundle options) { 122 verifyActivityInManifest(intent); 123 logStartedActivity(intent, requestCode, options); 124 return null; 125 } 126 logStartedActivity(Intent intent, int requestCode, Bundle options)127 private void logStartedActivity(Intent intent, int requestCode, Bundle options) { 128 startedActivities.add(intent); 129 intentRequestCodeMap.put(new FilterComparison(intent), requestCode); 130 startedActivitiesForResults.add(new IntentForResult(intent, requestCode, options)); 131 } 132 verifyActivityInManifest(Intent intent)133 private void verifyActivityInManifest(Intent intent) { 134 if (checkActivities 135 && RuntimeEnvironment.application.getPackageManager().resolveActivity(intent, -1) == null) { 136 throw new ActivityNotFoundException(intent.getAction()); 137 } 138 } 139 140 @Implementation execStartActivities( Context who, IBinder contextThread, IBinder token, Activity target, Intent[] intents, Bundle options)141 protected void execStartActivities( 142 Context who, 143 IBinder contextThread, 144 IBinder token, 145 Activity target, 146 Intent[] intents, 147 Bundle options) { 148 for (Intent intent : intents) { 149 execStartActivity(who, contextThread, token, target, intent, -1, options); 150 } 151 } 152 153 @Implementation(minSdk = LOLLIPOP) execStartActivityFromAppTask( Context who, IBinder contextThread, Object appTask, Intent intent, Bundle options)154 protected void execStartActivityFromAppTask( 155 Context who, IBinder contextThread, Object appTask, Intent intent, Bundle options) { 156 throw new UnsupportedOperationException("Implement me!!"); 157 } 158 159 @Implementation(minSdk = M) execStartActivity( Context who, IBinder contextThread, IBinder token, String target, Intent intent, int requestCode, Bundle options)160 protected ActivityResult execStartActivity( 161 Context who, 162 IBinder contextThread, 163 IBinder token, 164 String target, 165 Intent intent, 166 int requestCode, 167 Bundle options) { 168 verifyActivityInManifest(intent); 169 logStartedActivity(intent, requestCode, options); 170 171 return directlyOn(realObject, Instrumentation.class) 172 .execStartActivity(who, contextThread, token, target, intent, requestCode, options); 173 } 174 175 @Implementation(minSdk = JELLY_BEAN_MR1) execStartActivity( Context who, IBinder contextThread, IBinder token, String resultWho, Intent intent, int requestCode, Bundle options, UserHandle user)176 protected ActivityResult execStartActivity( 177 Context who, 178 IBinder contextThread, 179 IBinder token, 180 String resultWho, 181 Intent intent, 182 int requestCode, 183 Bundle options, 184 UserHandle user) { 185 throw new UnsupportedOperationException("Implement me!!"); 186 } 187 188 @Implementation(minSdk = M) execStartActivityAsCaller( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options, boolean ignoreTargetSecurity, int userId)189 protected ActivityResult execStartActivityAsCaller( 190 Context who, 191 IBinder contextThread, 192 IBinder token, 193 Activity target, 194 Intent intent, 195 int requestCode, 196 Bundle options, 197 boolean ignoreTargetSecurity, 198 int userId) { 199 throw new UnsupportedOperationException("Implement me!!"); 200 } 201 sendOrderedBroadcast( Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras, Context context)202 void sendOrderedBroadcast( 203 Intent intent, 204 String receiverPermission, 205 BroadcastReceiver resultReceiver, 206 Handler scheduler, 207 int initialCode, 208 String initialData, 209 Bundle initialExtras, 210 Context context) { 211 List<Wrapper> receivers = getAppropriateWrappers(intent, receiverPermission); 212 sortByPriority(receivers); 213 receivers.add(new Wrapper(resultReceiver, null, context, null, scheduler)); 214 postOrderedToWrappers(receivers, intent, initialCode, initialData, initialExtras, context); 215 } 216 assertNoBroadcastListenersOfActionRegistered(ContextWrapper context, String action)217 void assertNoBroadcastListenersOfActionRegistered(ContextWrapper context, String action) { 218 for (Wrapper registeredReceiver : registeredReceivers) { 219 if (registeredReceiver.context == context.getBaseContext()) { 220 Iterator<String> actions = registeredReceiver.intentFilter.actionsIterator(); 221 while (actions.hasNext()) { 222 if (actions.next().equals(action)) { 223 RuntimeException e = 224 new IllegalStateException( 225 "Unexpected BroadcastReceiver on " 226 + context 227 + " with action " 228 + action 229 + " " 230 + registeredReceiver.broadcastReceiver 231 + " that was originally registered here:"); 232 e.setStackTrace(registeredReceiver.exception.getStackTrace()); 233 throw e; 234 } 235 } 236 } 237 } 238 } 239 240 /** Returns the BroadcaseReceivers wrappers, matching intent's action and permissions. */ getAppropriateWrappers(Intent intent, String receiverPermission)241 private List<Wrapper> getAppropriateWrappers(Intent intent, String receiverPermission) { 242 broadcastIntents.add(intent); 243 244 List<Wrapper> result = new ArrayList<>(); 245 246 List<Wrapper> copy = new ArrayList<>(); 247 copy.addAll(registeredReceivers); 248 for (Wrapper wrapper : copy) { 249 if (hasMatchingPermission(wrapper.broadcastPermission, receiverPermission) 250 && wrapper.intentFilter.matchAction(intent.getAction())) { 251 final int match = 252 wrapper.intentFilter.matchData(intent.getType(), intent.getScheme(), intent.getData()); 253 if (match != IntentFilter.NO_MATCH_DATA && match != IntentFilter.NO_MATCH_TYPE) { 254 result.add(wrapper); 255 } 256 } 257 } 258 return result; 259 } 260 postIntent( Intent intent, Wrapper wrapper, final AtomicBoolean abort, Context context)261 private void postIntent( 262 Intent intent, Wrapper wrapper, final AtomicBoolean abort, Context context) { 263 final Handler scheduler = 264 (wrapper.scheduler != null) ? wrapper.scheduler : getMainHandler(context); 265 final BroadcastReceiver receiver = wrapper.broadcastReceiver; 266 final ShadowBroadcastReceiver shReceiver = Shadow.extract(receiver); 267 final Intent broadcastIntent = intent; 268 scheduler.post( 269 new Runnable() { 270 @Override 271 public void run() { 272 receiver.setPendingResult(ShadowBroadcastPendingResult.create(0, null, null, false)); 273 shReceiver.onReceive(context, broadcastIntent, abort); 274 } 275 }); 276 } 277 postToWrappers(List<Wrapper> wrappers, Intent intent, Context context)278 private void postToWrappers(List<Wrapper> wrappers, Intent intent, Context context) { 279 AtomicBoolean abort = 280 new AtomicBoolean(false); // abort state is shared among all broadcast receivers 281 for (Wrapper wrapper : wrappers) { 282 postIntent(intent, wrapper, abort, context); 283 } 284 } 285 postOrderedToWrappers( List<Wrapper> wrappers, final Intent intent, int initialCode, String data, Bundle extras, final Context context)286 private void postOrderedToWrappers( 287 List<Wrapper> wrappers, 288 final Intent intent, 289 int initialCode, 290 String data, 291 Bundle extras, 292 final Context context) { 293 final AtomicBoolean abort = 294 new AtomicBoolean(false); // abort state is shared among all broadcast receivers 295 ListenableFuture<BroadcastResultHolder> future = 296 immediateFuture(new BroadcastResultHolder(initialCode, data, extras)); 297 for (final Wrapper wrapper : wrappers) { 298 future = postIntent(wrapper, intent, future, abort, context); 299 } 300 final ListenableFuture<?> finalFuture = future; 301 future.addListener( 302 new Runnable() { 303 @Override 304 public void run() { 305 getMainHandler(context) 306 .post( 307 new Runnable() { 308 @Override 309 public void run() { 310 try { 311 finalFuture.get(); 312 } catch (InterruptedException | ExecutionException e) { 313 throw new RuntimeException(e); 314 } 315 } 316 }); 317 } 318 }, 319 directExecutor()); 320 } 321 322 /** 323 * Enforces that BroadcastReceivers invoked during an ordered broadcast run serially, passing 324 * along their results. 325 */ postIntent( final Wrapper wrapper, final Intent intent, ListenableFuture<BroadcastResultHolder> oldResult, final AtomicBoolean abort, final Context context)326 private ListenableFuture<BroadcastResultHolder> postIntent( 327 final Wrapper wrapper, 328 final Intent intent, 329 ListenableFuture<BroadcastResultHolder> oldResult, 330 final AtomicBoolean abort, 331 final Context context) { 332 final Handler scheduler = 333 (wrapper.scheduler != null) ? wrapper.scheduler : getMainHandler(context); 334 return Futures.transformAsync( 335 oldResult, 336 new AsyncFunction<BroadcastResultHolder, BroadcastResultHolder>() { 337 @Override 338 public ListenableFuture<BroadcastResultHolder> apply( 339 BroadcastResultHolder broadcastResultHolder) throws Exception { 340 final BroadcastReceiver.PendingResult result = 341 ShadowBroadcastPendingResult.create( 342 broadcastResultHolder.resultCode, 343 broadcastResultHolder.resultData, 344 broadcastResultHolder.resultExtras, 345 true /*ordered */); 346 wrapper.broadcastReceiver.setPendingResult(result); 347 scheduler.post( 348 () -> { 349 ShadowBroadcastReceiver shadowBroadcastReceiver = 350 Shadow.extract(wrapper.broadcastReceiver); 351 shadowBroadcastReceiver.onReceive(context, intent, abort); 352 }); 353 return BroadcastResultHolder.transform(result); 354 } 355 }, 356 directExecutor()); 357 } 358 359 /** 360 * Broadcasts the {@code Intent} by iterating through the registered receivers, invoking their 361 * filters including permissions, and calling {@code onReceive(Application, Intent)} as 362 * appropriate. Does not enqueue the {@code Intent} for later inspection. 363 * 364 * @param context 365 * @param intent the {@code Intent} to broadcast todo: enqueue the Intent for later inspection 366 */ 367 void sendBroadcastWithPermission(Intent intent, String receiverPermission, Context context) { 368 List<Wrapper> wrappers = getAppropriateWrappers(intent, receiverPermission); 369 postToWrappers(wrappers, intent, context); 370 } 371 372 void sendOrderedBroadcastWithPermission( 373 Intent intent, String receiverPermission, Context context) { 374 List<Wrapper> wrappers = getAppropriateWrappers(intent, receiverPermission); 375 // sort by the decrease of priorities 376 sortByPriority(wrappers); 377 378 postOrderedToWrappers(wrappers, intent, 0, null, null, context); 379 } 380 381 private void sortByPriority(List<Wrapper> wrappers) { 382 Collections.sort( 383 wrappers, 384 new Comparator<Wrapper>() { 385 @Override 386 public int compare(Wrapper o1, Wrapper o2) { 387 return Integer.compare( 388 o2.getIntentFilter().getPriority(), o1.getIntentFilter().getPriority()); 389 } 390 }); 391 } 392 393 List<Intent> getBroadcastIntents() { 394 return broadcastIntents; 395 } 396 397 Intent getNextStartedActivity() { 398 if (startedActivities.isEmpty()) { 399 return null; 400 } else { 401 return startedActivities.remove(startedActivities.size() - 1); 402 } 403 } 404 405 Intent peekNextStartedActivity() { 406 if (startedActivities.isEmpty()) { 407 return null; 408 } else { 409 return startedActivities.get(startedActivities.size() - 1); 410 } 411 } 412 413 /** 414 * Clears all {@code Intent}s started by {@link #execStartActivity(Context, IBinder, IBinder, 415 * Activity, Intent, int, Bundle)}, {@link #execStartActivity(Context, IBinder, IBinder, Fragment, 416 * Intent, int, Bundle)}, and {@link #execStartActivity(Context, IBinder, IBinder, String, Intent, 417 * int, Bundle)}. 418 */ 419 void clearNextStartedActivities() { 420 startedActivities.clear(); 421 } 422 423 IntentForResult getNextStartedActivityForResult() { 424 if (startedActivitiesForResults.isEmpty()) { 425 return null; 426 } else { 427 return startedActivitiesForResults.remove(startedActivitiesForResults.size() - 1); 428 } 429 } 430 431 IntentForResult peekNextStartedActivityForResult() { 432 if (startedActivitiesForResults.isEmpty()) { 433 return null; 434 } else { 435 return startedActivitiesForResults.get(startedActivitiesForResults.size() - 1); 436 } 437 } 438 439 void checkActivities(boolean checkActivities) { 440 this.checkActivities = checkActivities; 441 } 442 443 int getRequestCodeForIntent(Intent requestIntent) { 444 Integer requestCode = intentRequestCodeMap.get(new Intent.FilterComparison(requestIntent)); 445 if (requestCode == null) { 446 throw new RuntimeException( 447 "No intent matches " + requestIntent + " among " + intentRequestCodeMap.keySet()); 448 } 449 return requestCode; 450 } 451 452 protected ComponentName startService(Intent intent) { 453 startedServices.add(new Intent.FilterComparison(intent)); 454 if (intent.getComponent() != null) { 455 return intent.getComponent(); 456 } 457 return new ComponentName("some.service.package", "SomeServiceName-FIXME"); 458 } 459 460 boolean stopService(Intent name) { 461 stoppedServices.add(new Intent.FilterComparison(name)); 462 return startedServices.contains(new Intent.FilterComparison(name)); 463 } 464 465 void setComponentNameAndServiceForBindService(ComponentName name, IBinder service) { 466 defaultServiceConnectionData = new ServiceConnectionDataWrapper(name, service); 467 } 468 469 void setComponentNameAndServiceForBindServiceForIntent( 470 Intent intent, ComponentName name, IBinder service) { 471 serviceConnectionDataForIntent.put( 472 new Intent.FilterComparison(intent), new ServiceConnectionDataWrapper(name, service)); 473 } 474 475 protected boolean bindService( 476 final Intent intent, final ServiceConnection serviceConnection, int i) { 477 boundServiceConnections.add(serviceConnection); 478 unboundServiceConnections.remove(serviceConnection); 479 if (unbindableActions.contains(intent.getAction())) { 480 return false; 481 } 482 startedServices.add(new Intent.FilterComparison(intent)); 483 ShadowLooper shadowLooper = Shadow.extract(Looper.getMainLooper()); 484 shadowLooper.post( 485 () -> { 486 final ServiceConnectionDataWrapper serviceConnectionDataWrapper; 487 final Intent.FilterComparison filterComparison = new Intent.FilterComparison(intent); 488 if (serviceConnectionDataForIntent.containsKey(filterComparison)) { 489 serviceConnectionDataWrapper = serviceConnectionDataForIntent.get(filterComparison); 490 } else { 491 serviceConnectionDataWrapper = defaultServiceConnectionData; 492 } 493 serviceConnectionDataForServiceConnection.put( 494 serviceConnection, serviceConnectionDataWrapper); 495 serviceConnection.onServiceConnected( 496 serviceConnectionDataWrapper.componentNameForBindService, 497 serviceConnectionDataWrapper.binderForBindService); 498 }, 499 0); 500 return true; 501 } 502 503 protected void unbindService(final ServiceConnection serviceConnection) { 504 if (unbindServiceShouldThrowIllegalArgument) { 505 throw new IllegalArgumentException(); 506 } 507 508 unboundServiceConnections.add(serviceConnection); 509 boundServiceConnections.remove(serviceConnection); 510 ShadowLooper shadowLooper = Shadow.extract(Looper.getMainLooper()); 511 shadowLooper.post( 512 () -> { 513 final ServiceConnectionDataWrapper serviceConnectionDataWrapper; 514 if (serviceConnectionDataForServiceConnection.containsKey(serviceConnection)) { 515 serviceConnectionDataWrapper = 516 serviceConnectionDataForServiceConnection.get(serviceConnection); 517 } else { 518 serviceConnectionDataWrapper = defaultServiceConnectionData; 519 } 520 serviceConnection.onServiceDisconnected( 521 serviceConnectionDataWrapper.componentNameForBindService); 522 }, 523 0); 524 } 525 526 protected List<ServiceConnection> getBoundServiceConnections() { 527 return boundServiceConnections; 528 } 529 530 void setUnbindServiceShouldThrowIllegalArgument(boolean flag) { 531 unbindServiceShouldThrowIllegalArgument = flag; 532 } 533 534 protected List<ServiceConnection> getUnboundServiceConnections() { 535 return unboundServiceConnections; 536 } 537 538 void declareActionUnbindable(String action) { 539 unbindableActions.add(action); 540 } 541 542 public List<String> getUnbindableActions() { 543 return unbindableActions; 544 } 545 546 /** 547 * Consumes the most recent {@code Intent} started by {@link 548 * #startService(android.content.Intent)} and returns it. 549 * 550 * @return the most recently started {@code Intent} 551 */ 552 Intent getNextStartedService() { 553 if (startedServices.isEmpty()) { 554 return null; 555 } else { 556 return startedServices.remove(0).getIntent(); 557 } 558 } 559 560 /** 561 * Returns the most recent {@code Intent} started by {@link #startService(android.content.Intent)} 562 * without consuming it. 563 * 564 * @return the most recently started {@code Intent} 565 */ 566 Intent peekNextStartedService() { 567 if (startedServices.isEmpty()) { 568 return null; 569 } else { 570 return startedServices.get(0).getIntent(); 571 } 572 } 573 574 /** Clears all {@code Intent} started by {@link #startService(android.content.Intent)}. */ 575 void clearStartedServices() { 576 startedServices.clear(); 577 } 578 579 /** 580 * Consumes the {@code Intent} requested to stop a service by {@link 581 * #stopService(android.content.Intent)} from the bottom of the stack of stop requests. 582 */ 583 Intent getNextStoppedService() { 584 if (stoppedServices.isEmpty()) { 585 return null; 586 } else { 587 return stoppedServices.remove(0).getIntent(); 588 } 589 } 590 591 void sendStickyBroadcast(Intent intent, Context context) { 592 stickyIntents.put(intent.getAction(), intent); 593 sendBroadcast(intent, context); 594 } 595 596 void sendBroadcast(Intent intent, Context context) { 597 sendBroadcastWithPermission(intent, null, context); 598 } 599 600 Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, Context context) { 601 return registerReceiver(receiver, filter, null, null, context); 602 } 603 604 Intent registerReceiver( 605 BroadcastReceiver receiver, 606 IntentFilter filter, 607 String broadcastPermission, 608 Handler scheduler, 609 Context context) { 610 return registerReceiverWithContext(receiver, filter, broadcastPermission, scheduler, context); 611 } 612 613 Intent registerReceiverWithContext( 614 BroadcastReceiver receiver, 615 IntentFilter filter, 616 String broadcastPermission, 617 Handler scheduler, 618 Context context) { 619 if (receiver != null) { 620 registeredReceivers.add( 621 new Wrapper(receiver, filter, context, broadcastPermission, scheduler)); 622 } 623 return processStickyIntents(filter, receiver, context); 624 } 625 626 private Intent processStickyIntents( 627 IntentFilter filter, BroadcastReceiver receiver, Context context) { 628 Intent result = null; 629 for (Intent stickyIntent : stickyIntents.values()) { 630 if (filter.matchAction(stickyIntent.getAction())) { 631 if (result == null) { 632 result = stickyIntent; 633 } 634 if (receiver != null) { 635 receiver.setPendingResult(ShadowBroadcastPendingResult.createSticky(stickyIntent)); 636 receiver.onReceive(context, stickyIntent); 637 receiver.setPendingResult(null); 638 } else if (result != null) { 639 break; 640 } 641 } 642 } 643 return result; 644 } 645 646 void unregisterReceiver(BroadcastReceiver broadcastReceiver) { 647 boolean found = false; 648 Iterator<Wrapper> iterator = registeredReceivers.iterator(); 649 while (iterator.hasNext()) { 650 Wrapper wrapper = iterator.next(); 651 if (wrapper.broadcastReceiver == broadcastReceiver) { 652 iterator.remove(); 653 found = true; 654 } 655 } 656 if (!found) { 657 throw new IllegalArgumentException("Receiver not registered: " + broadcastReceiver); 658 } 659 } 660 661 /** @deprecated use PackageManager.queryBroadcastReceivers instead */ 662 @Deprecated 663 boolean hasReceiverForIntent(Intent intent) { 664 for (Wrapper wrapper : registeredReceivers) { 665 if (wrapper.intentFilter.matchAction(intent.getAction())) { 666 return true; 667 } 668 } 669 return false; 670 } 671 672 /** @deprecated use PackageManager.queryBroadcastReceivers instead */ 673 @Deprecated 674 List<BroadcastReceiver> getReceiversForIntent(Intent intent) { 675 ArrayList<BroadcastReceiver> broadcastReceivers = new ArrayList<>(); 676 for (Wrapper wrapper : registeredReceivers) { 677 if (wrapper.intentFilter.matchAction(intent.getAction())) { 678 broadcastReceivers.add(wrapper.getBroadcastReceiver()); 679 } 680 } 681 return broadcastReceivers; 682 } 683 684 /** @return list of {@link Wrapper}s for registered receivers */ 685 List<Wrapper> getRegisteredReceivers() { 686 return registeredReceivers; 687 } 688 689 int checkPermission(String permission, int pid, int uid) { 690 Set<String> grantedPermissionsForPidUid = grantedPermissionsMap.get(new Pair(pid, uid)); 691 return grantedPermissionsForPidUid != null && grantedPermissionsForPidUid.contains(permission) 692 ? PERMISSION_GRANTED 693 : PERMISSION_DENIED; 694 } 695 696 void grantPermissions(String... permissionNames) { 697 grantPermissions(Process.myPid(), Process.myUid(), permissionNames); 698 } 699 700 void grantPermissions(int pid, int uid, String... permissions) { 701 Set<String> grantedPermissionsForPidUid = grantedPermissionsMap.get(new Pair<>(pid, uid)); 702 if (grantedPermissionsForPidUid == null) { 703 grantedPermissionsForPidUid = new HashSet<>(); 704 grantedPermissionsMap.put(new Pair<>(pid, uid), grantedPermissionsForPidUid); 705 } 706 Collections.addAll(grantedPermissionsForPidUid, permissions); 707 } 708 709 void denyPermissions(String... permissionNames) { 710 denyPermissions(Process.myPid(), Process.myUid(), permissionNames); 711 } 712 713 void denyPermissions(int pid, int uid, String... permissions) { 714 Set<String> grantedPermissionsForPidUid = grantedPermissionsMap.get(new Pair<>(pid, uid)); 715 if (grantedPermissionsForPidUid != null) { 716 for (String permissionName : permissions) { 717 grantedPermissionsForPidUid.remove(permissionName); 718 } 719 } 720 } 721 722 private boolean hasMatchingPermission(String permission1, String permission2) { 723 return permission1 == null ? permission2 == null : permission1.equals(permission2); 724 } 725 726 private Handler getMainHandler(Context context) { 727 if (mainHandler == null) { 728 mainHandler = new Handler(context.getMainLooper()); 729 } 730 return mainHandler; 731 } 732 733 734 735 736 private static final class BroadcastResultHolder { 737 private final int resultCode; 738 private final String resultData; 739 private final Bundle resultExtras; 740 741 private BroadcastResultHolder(int resultCode, String resultData, Bundle resultExtras) { 742 this.resultCode = resultCode; 743 this.resultData = resultData; 744 this.resultExtras = resultExtras; 745 } 746 747 private static ListenableFuture<BroadcastResultHolder> transform( 748 BroadcastReceiver.PendingResult result) { 749 ShadowBroadcastPendingResult shadowBroadcastPendingResult = Shadow.extract(result); 750 return Futures.transform( 751 shadowBroadcastPendingResult.getFuture(), 752 pendingResult -> 753 new BroadcastResultHolder( 754 pendingResult.getResultCode(), 755 pendingResult.getResultData(), 756 pendingResult.getResultExtras(false)), 757 directExecutor()); 758 } 759 } 760 761 private static class ServiceConnectionDataWrapper { 762 public final ComponentName componentNameForBindService; 763 public final IBinder binderForBindService; 764 765 private ServiceConnectionDataWrapper( 766 ComponentName componentNameForBindService, IBinder binderForBindService) { 767 this.componentNameForBindService = componentNameForBindService; 768 this.binderForBindService = binderForBindService; 769 } 770 } 771 772 public static Instrumentation getInstrumentation() { 773 ActivityThread activityThread = (ActivityThread) RuntimeEnvironment.getActivityThread(); 774 return activityThread.getInstrumentation(); 775 } 776 } 777