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