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