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