1 package org.robolectric.shadows;
2 
3 import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
4 import static android.app.PendingIntent.FLAG_IMMUTABLE;
5 import static android.app.PendingIntent.FLAG_NO_CREATE;
6 import static android.app.PendingIntent.FLAG_ONE_SHOT;
7 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
8 import static android.os.Build.VERSION_CODES.M;
9 import static android.os.Build.VERSION_CODES.N;
10 import static android.os.Build.VERSION_CODES.O;
11 import static android.os.Build.VERSION_CODES.S;
12 import static org.robolectric.RuntimeEnvironment.getApiLevel;
13 import static org.robolectric.util.reflector.Reflector.reflector;
14 
15 import android.annotation.NonNull;
16 import android.annotation.SuppressLint;
17 import android.app.Activity;
18 import android.app.ActivityThread;
19 import android.app.PendingIntent;
20 import android.app.PendingIntent.CanceledException;
21 import android.app.PendingIntent.OnMarshaledListener;
22 import android.content.Context;
23 import android.content.IIntentSender;
24 import android.content.Intent;
25 import android.content.IntentSender;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.IBinder;
29 import android.os.Parcel;
30 import android.os.Parcelable.Creator;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Objects;
36 import javax.annotation.Nullable;
37 import javax.annotation.concurrent.GuardedBy;
38 import org.robolectric.RuntimeEnvironment;
39 import org.robolectric.annotation.Implementation;
40 import org.robolectric.annotation.Implements;
41 import org.robolectric.annotation.RealObject;
42 import org.robolectric.annotation.Resetter;
43 import org.robolectric.fakes.RoboIntentSender;
44 import org.robolectric.shadow.api.Shadow;
45 import org.robolectric.util.ReflectionHelpers;
46 import org.robolectric.util.reflector.Accessor;
47 import org.robolectric.util.reflector.ForType;
48 import org.robolectric.versioning.AndroidVersions.V;
49 
50 @Implements(PendingIntent.class)
51 @SuppressLint("NewApi")
52 public class ShadowPendingIntent {
53 
54   private enum Type {
55     ACTIVITY,
56     BROADCAST,
57     SERVICE,
58     FOREGROUND_SERVICE
59   }
60 
61   private static final int NULL_PENDING_INTENT_VALUE = -1;
62 
63   @GuardedBy("lock")
64   private static final List<PendingIntent> createdIntents = new ArrayList<>();
65 
66   private static final Object lock = new Object();
67 
68   private static final List<PendingIntent> parceledPendingIntents = new ArrayList<>();
69 
70   @RealObject private PendingIntent realPendingIntent;
71 
72   @NonNull private Intent[] savedIntents;
73   private Context savedContext;
74   private Type type;
75   private int requestCode;
76   private int flags;
77   @Nullable private Bundle options;
78   private String creatorPackage;
79   private int creatorUid;
80   private boolean canceled;
81   @Nullable private PendingIntent.OnFinished lastOnFinished;
82 
83   @Implementation
__staticInitializer__()84   protected static void __staticInitializer__() {
85     Shadow.directInitialize(PendingIntent.class);
86     ReflectionHelpers.setStaticField(PendingIntent.class, "CREATOR", ShadowPendingIntent.CREATOR);
87   }
88 
89   @Implementation
getActivity( Context context, int requestCode, @NonNull Intent intent, int flags)90   protected static PendingIntent getActivity(
91       Context context, int requestCode, @NonNull Intent intent, int flags) {
92     return create(context, new Intent[] {intent}, Type.ACTIVITY, requestCode, flags, null);
93   }
94 
95   @Implementation
getActivity( Context context, int requestCode, @NonNull Intent intent, int flags, Bundle options)96   protected static PendingIntent getActivity(
97       Context context, int requestCode, @NonNull Intent intent, int flags, Bundle options) {
98     return create(context, new Intent[] {intent}, Type.ACTIVITY, requestCode, flags, options);
99   }
100 
101   @Implementation
getActivities( Context context, int requestCode, @NonNull Intent[] intents, int flags)102   protected static PendingIntent getActivities(
103       Context context, int requestCode, @NonNull Intent[] intents, int flags) {
104     return create(context, intents, Type.ACTIVITY, requestCode, flags, null);
105   }
106 
107   @Implementation
getActivities( Context context, int requestCode, @NonNull Intent[] intents, int flags, Bundle options)108   protected static PendingIntent getActivities(
109       Context context, int requestCode, @NonNull Intent[] intents, int flags, Bundle options) {
110     return create(context, intents, Type.ACTIVITY, requestCode, flags, options);
111   }
112 
113   @Implementation
getBroadcast( Context context, int requestCode, @NonNull Intent intent, int flags)114   protected static PendingIntent getBroadcast(
115       Context context, int requestCode, @NonNull Intent intent, int flags) {
116     return create(context, new Intent[] {intent}, Type.BROADCAST, requestCode, flags, null);
117   }
118 
119   @Implementation
getService( Context context, int requestCode, @NonNull Intent intent, int flags)120   protected static PendingIntent getService(
121       Context context, int requestCode, @NonNull Intent intent, int flags) {
122     return create(context, new Intent[] {intent}, Type.SERVICE, requestCode, flags, null);
123   }
124 
125   @Implementation(minSdk = O)
getForegroundService( Context context, int requestCode, @NonNull Intent intent, int flags)126   protected static PendingIntent getForegroundService(
127       Context context, int requestCode, @NonNull Intent intent, int flags) {
128     return create(
129         context, new Intent[] {intent}, Type.FOREGROUND_SERVICE, requestCode, flags, null);
130   }
131 
132   @Implementation
133   @SuppressWarnings("ReferenceEquality")
cancel()134   protected void cancel() {
135     synchronized (lock) {
136       for (Iterator<PendingIntent> i = createdIntents.iterator(); i.hasNext(); ) {
137         PendingIntent pendingIntent = i.next();
138         if (pendingIntent == realPendingIntent) {
139           canceled = true;
140           i.remove();
141           break;
142         }
143       }
144     }
145   }
146 
147   @Implementation
send()148   protected void send() throws CanceledException {
149     send(savedContext, 0, null);
150   }
151 
152   @Implementation
send(int code)153   protected void send(int code) throws CanceledException {
154     send(savedContext, code, null);
155   }
156 
157   @Implementation
send(int code, PendingIntent.OnFinished onFinished, Handler handler)158   protected void send(int code, PendingIntent.OnFinished onFinished, Handler handler)
159       throws CanceledException {
160     send(savedContext, code, null, onFinished, handler);
161   }
162 
163   @Implementation
send(Context context, int code, Intent intent)164   protected void send(Context context, int code, Intent intent) throws CanceledException {
165     send(context, code, intent, null, null);
166   }
167 
168   @Implementation
send( Context context, int code, Intent intent, PendingIntent.OnFinished onFinished, Handler handler)169   protected void send(
170       Context context,
171       int code,
172       Intent intent,
173       PendingIntent.OnFinished onFinished,
174       Handler handler)
175       throws CanceledException {
176     send(context, code, intent, onFinished, handler, null);
177   }
178 
179   @Implementation
send( Context context, int code, Intent intent, PendingIntent.OnFinished onFinished, Handler handler, String requiredPermission)180   protected void send(
181       Context context,
182       int code,
183       Intent intent,
184       PendingIntent.OnFinished onFinished,
185       Handler handler,
186       String requiredPermission)
187       throws CanceledException {
188     // Manually propagating to keep only one implementation regardless of SDK
189     send(context, code, intent, onFinished, handler, requiredPermission, null);
190   }
191 
192   @Implementation(minSdk = M)
send( Context context, int code, Intent intent, PendingIntent.OnFinished onFinished, Handler handler, String requiredPermission, Bundle options)193   protected void send(
194       Context context,
195       int code,
196       Intent intent,
197       PendingIntent.OnFinished onFinished,
198       Handler handler,
199       String requiredPermission,
200       Bundle options)
201       throws CanceledException {
202     send(context, code, intent, onFinished, handler, requiredPermission, options, 0);
203   }
204 
send( Context context, int code, Intent intent, PendingIntent.OnFinished onFinished, Handler handler, String requiredPermission, Bundle options, int requestCode)205   void send(
206       Context context,
207       int code,
208       Intent intent,
209       PendingIntent.OnFinished onFinished,
210       Handler handler,
211       String requiredPermission,
212       Bundle options,
213       int requestCode)
214       throws CanceledException {
215     this.lastOnFinished =
216         handler == null
217             ? onFinished
218             : (pendingIntent, intent1, resultCode, resultData, resultExtras) ->
219                 handler.post(
220                     () ->
221                         onFinished.onSendFinished(
222                             pendingIntent, intent1, resultCode, resultData, resultExtras));
223 
224     if (canceled) {
225       throw new CanceledException();
226     }
227 
228     // Fill in the last Intent, if it is mutable, with information now available at send-time.
229     Intent[] intentsToSend;
230     if (intent != null && isMutable(flags)) {
231       // Copy the last intent before filling it in to avoid modifying this PendingIntent.
232       intentsToSend = Arrays.copyOf(savedIntents, savedIntents.length);
233       Intent lastIntentCopy = new Intent(intentsToSend[intentsToSend.length - 1]);
234       lastIntentCopy.fillIn(intent, flags);
235       intentsToSend[intentsToSend.length - 1] = lastIntentCopy;
236     } else {
237       intentsToSend = savedIntents;
238     }
239 
240     ActivityThread activityThread = (ActivityThread) RuntimeEnvironment.getActivityThread();
241     ShadowInstrumentation shadowInstrumentation =
242         Shadow.extract(activityThread.getInstrumentation());
243     if (isActivity()) {
244       for (Intent intentToSend : intentsToSend) {
245         shadowInstrumentation.execStartActivity(
246             context,
247             (IBinder) null,
248             (IBinder) null,
249             (Activity) null,
250             intentToSend,
251             requestCode,
252             options);
253       }
254     } else if (isBroadcast()) {
255       for (Intent intentToSend : intentsToSend) {
256         shadowInstrumentation.sendBroadcastWithPermission(
257             intentToSend, requiredPermission, context, options, code);
258       }
259     } else if (isService()) {
260       for (Intent intentToSend : intentsToSend) {
261         context.startService(intentToSend);
262       }
263     } else if (isForegroundService()) {
264       for (Intent intentToSend : intentsToSend) {
265         context.startForegroundService(intentToSend);
266       }
267     }
268 
269     if (isOneShot(flags)) {
270       cancel();
271     }
272   }
273 
274   @Implementation
getIntentSender()275   protected IntentSender getIntentSender() {
276     return new RoboIntentSender(realPendingIntent);
277   }
278 
279   /**
280    * Returns {@code true} if this {@code PendingIntent} was created with {@link #getActivity} or
281    * {@link #getActivities}.
282    *
283    * <p>This method is intentionally left {@code public} rather than {@code protected} because it
284    * serves a secondary purpose as a utility shadow method for API levels < 31.
285    */
286   @Implementation(minSdk = S)
isActivity()287   public boolean isActivity() {
288     return type == Type.ACTIVITY;
289   }
290 
291   /**
292    * Returns {@code true} if this {@code PendingIntent} was created with {@link #getBroadcast}.
293    *
294    * <p>This method is intentionally left {@code public} rather than {@code protected} because it
295    * serves a secondary purpose as a utility shadow method for API levels < 31.
296    */
297   @Implementation(minSdk = S)
isBroadcast()298   public boolean isBroadcast() {
299     return type == Type.BROADCAST;
300   }
301 
302   /**
303    * Returns {@code true} if this {@code PendingIntent} was created with {@link
304    * #getForegroundService}.
305    *
306    * <p>This method is intentionally left {@code public} rather than {@code protected} because it
307    * serves a secondary purpose as a utility shadow method for API levels < 31.
308    */
309   @Implementation(minSdk = S)
isForegroundService()310   public boolean isForegroundService() {
311     return type == Type.FOREGROUND_SERVICE;
312   }
313 
314   /**
315    * Returns {@code true} if this {@code PendingIntent} was created with {@link #getService}.
316    *
317    * <p>This method is intentionally left {@code public} rather than {@code protected} because it
318    * serves a secondary purpose as a utility shadow method for API levels < 31.
319    */
320   @Implementation(minSdk = S)
isService()321   public boolean isService() {
322     return type == Type.SERVICE;
323   }
324 
325   /**
326    * Returns {@code true} if this {@code PendingIntent} is marked with {@link
327    * PendingIntent#FLAG_IMMUTABLE}.
328    *
329    * <p>This method is intentionally left {@code public} rather than {@code protected} because it
330    * serves a secondary purpose as a utility shadow method for API levels < 31.
331    */
332   @Implementation(minSdk = S)
isImmutable()333   public boolean isImmutable() {
334     return (flags & FLAG_IMMUTABLE) > 0;
335   }
336 
337   @Implementation
isTargetedToPackage()338   protected boolean isTargetedToPackage() {
339     // This is weird and we know it. See:
340     // https://googleplex-android.googlesource.com/platform/frameworks/base/+/f24a737c89de326199eb6d9f5912eae24b5514e6/services/core/java/com/android/server/am/ActivityManagerService.java#5377
341     for (Intent intent : savedIntents) {
342       if (intent.getPackage() != null && intent.getComponent() != null) {
343         return false;
344       }
345     }
346     return true;
347   }
348 
349   /**
350    * @return {@code true} iff sending this PendingIntent will start an activity
351    * @deprecated prefer {@link #isActivity} which was added to {@link PendingIntent} in API 31
352    *     (Android S).
353    */
354   @Deprecated
isActivityIntent()355   public boolean isActivityIntent() {
356     return type == Type.ACTIVITY;
357   }
358 
359   /**
360    * @return {@code true} iff sending this PendingIntent will broadcast an Intent
361    * @deprecated prefer {@link #isBroadcast} which was added to {@link PendingIntent} in API 31
362    *     (Android S).
363    */
364   @Deprecated
isBroadcastIntent()365   public boolean isBroadcastIntent() {
366     return type == Type.BROADCAST;
367   }
368 
369   /**
370    * @return {@code true} iff sending this PendingIntent will start a service
371    * @deprecated prefer {@link #isService} which was added to {@link PendingIntent} in API 31
372    *     (Android S).
373    */
374   @Deprecated
isServiceIntent()375   public boolean isServiceIntent() {
376     return type == Type.SERVICE;
377   }
378 
379   /**
380    * @return {@code true} iff sending this PendingIntent will start a foreground service
381    * @deprecated prefer {@link #isForegroundService} which was added to {@link PendingIntent} in API
382    *     31 (Android S).
383    */
384   @Deprecated
isForegroundServiceIntent()385   public boolean isForegroundServiceIntent() {
386     return type == Type.FOREGROUND_SERVICE;
387   }
388 
389   /**
390    * @return the context in which this PendingIntent was created
391    */
getSavedContext()392   public Context getSavedContext() {
393     return savedContext;
394   }
395 
396   /**
397    * This returns the last Intent in the Intent[] to be delivered when the PendingIntent is sent.
398    * This method is particularly useful for PendingIntents created with a single Intent:
399    *
400    * <ul>
401    *   <li>{@link #getActivity(Context, int, Intent, int)}
402    *   <li>{@link #getActivity(Context, int, Intent, int, Bundle)}
403    *   <li>{@link #getBroadcast(Context, int, Intent, int)}
404    *   <li>{@link #getService(Context, int, Intent, int)}
405    * </ul>
406    *
407    * @return the final Intent to be delivered when the PendingIntent is sent
408    */
getSavedIntent()409   public Intent getSavedIntent() {
410     return savedIntents[savedIntents.length - 1];
411   }
412 
413   /**
414    * This method is particularly useful for PendingIntents created with multiple Intents:
415    *
416    * <ul>
417    *   <li>{@link #getActivities(Context, int, Intent[], int)}
418    *   <li>{@link #getActivities(Context, int, Intent[], int, Bundle)}
419    * </ul>
420    *
421    * @return all Intents to be delivered when the PendingIntent is sent
422    */
getSavedIntents()423   public Intent[] getSavedIntents() {
424     return savedIntents;
425   }
426 
427   /**
428    * @return {@true} iff this PendingIntent has been canceled
429    */
isCanceled()430   public boolean isCanceled() {
431     return canceled;
432   }
433 
434   /**
435    * @return the request code with which this PendingIntent was created
436    */
getRequestCode()437   public int getRequestCode() {
438     return requestCode;
439   }
440 
441   /**
442    * @return the flags with which this PendingIntent was created
443    */
getFlags()444   public int getFlags() {
445     return flags;
446   }
447 
448   /**
449    * @return the flags with which this PendingIntent was created
450    */
getOptions()451   public @Nullable Bundle getOptions() {
452     return options;
453   }
454 
455   /**
456    * Calls {@link PendingIntent.OnFinished#onSendFinished} on the last {@link
457    * PendingIntent.OnFinished} passed with {@link #send()}.
458    *
459    * <p>{@link PendingIntent.OnFinished#onSendFinished} is called on the {@link Handler} passed with
460    * {@link #send()} (if any). If no {@link Handler} was provided it's invoked on the calling
461    * thread.
462    *
463    * @return false if no {@link PendingIntent.OnFinished} callback was passed with the last {@link
464    *     #send()} call, true otherwise.
465    */
callLastOnFinished( Intent intent, int resultCode, String resultData, Bundle resultExtras)466   public boolean callLastOnFinished(
467       Intent intent, int resultCode, String resultData, Bundle resultExtras) {
468     if (lastOnFinished == null) {
469       return false;
470     }
471 
472     lastOnFinished.onSendFinished(realPendingIntent, intent, resultCode, resultData, resultExtras);
473     return true;
474   }
475 
476   @Implementation
getTargetPackage()477   protected String getTargetPackage() {
478     return getCreatorPackage();
479   }
480 
481   @Implementation
getCreatorPackage()482   protected String getCreatorPackage() {
483     return (creatorPackage == null)
484         ? RuntimeEnvironment.getApplication().getPackageName()
485         : creatorPackage;
486   }
487 
setCreatorPackage(String creatorPackage)488   public void setCreatorPackage(String creatorPackage) {
489     this.creatorPackage = creatorPackage;
490   }
491 
492   @Implementation
getCreatorUid()493   protected int getCreatorUid() {
494     return creatorUid;
495   }
496 
setCreatorUid(int uid)497   public void setCreatorUid(int uid) {
498     this.creatorUid = uid;
499   }
500 
501   @Override
502   @Implementation
equals(Object o)503   public boolean equals(Object o) {
504     if (this == o) return true;
505     if (o == null || realPendingIntent.getClass() != o.getClass()) return false;
506     ShadowPendingIntent that = Shadow.extract((PendingIntent) o);
507 
508     String packageName = savedContext == null ? null : savedContext.getPackageName();
509     String thatPackageName = that.savedContext == null ? null : that.savedContext.getPackageName();
510     if (!Objects.equals(packageName, thatPackageName)) {
511       return false;
512     }
513 
514     if (this.savedIntents.length != that.savedIntents.length) {
515       return false;
516     }
517 
518     for (int i = 0; i < this.savedIntents.length; i++) {
519       if (!this.savedIntents[i].filterEquals(that.savedIntents[i])) {
520         return false;
521       }
522     }
523 
524     if (this.requestCode != that.requestCode) {
525       return false;
526     }
527     return true;
528   }
529 
530   @Override
531   @Implementation
hashCode()532   public int hashCode() {
533     int result = savedIntents != null ? Arrays.hashCode(savedIntents) : 0;
534     if (savedContext != null) {
535       String packageName = savedContext.getPackageName();
536       result = 31 * result + (packageName != null ? packageName.hashCode() : 0);
537     }
538     result = 31 * result + requestCode;
539     return result;
540   }
541 
542   @Implementation
543   @Nullable
readPendingIntentOrNullFromParcel(@onNull Parcel in)544   public static PendingIntent readPendingIntentOrNullFromParcel(@NonNull Parcel in) {
545     int intentIndex = in.readInt();
546     if (intentIndex == NULL_PENDING_INTENT_VALUE) {
547       return null;
548     }
549     return parceledPendingIntents.get(intentIndex);
550   }
551 
552   @Implementation
writePendingIntentOrNullToParcel( @ullable PendingIntent sender, @NonNull Parcel out)553   public static void writePendingIntentOrNullToParcel(
554       @Nullable PendingIntent sender, @NonNull Parcel out) {
555     if (sender == null) {
556       out.writeInt(NULL_PENDING_INTENT_VALUE);
557       return;
558     }
559 
560     int index = parceledPendingIntents.size();
561     parceledPendingIntents.add(sender);
562     out.writeInt(index);
563 
564     if (getApiLevel() >= V.SDK_INT) {
565       ThreadLocal<List<OnMarshaledListener>> sOnMarshaledListeners =
566           ReflectionHelpers.getStaticField(PendingIntent.class, "sOnMarshaledListener");
567       List<OnMarshaledListener> listeners = sOnMarshaledListeners.get();
568       if (listeners != null) {
569         for (OnMarshaledListener listener : listeners) {
570           listener.onMarshaled(sender, out, 0);
571         }
572       }
573     } else if (getApiLevel() >= N) {
574       ThreadLocal<OnMarshaledListener> sOnMarshaledListener =
575           ReflectionHelpers.getStaticField(PendingIntent.class, "sOnMarshaledListener");
576       OnMarshaledListener listener = sOnMarshaledListener.get();
577       if (listener != null) {
578         listener.onMarshaled(sender, out, 0);
579       }
580     }
581   }
582 
583   static final Creator<PendingIntent> CREATOR =
584       new Creator<PendingIntent>() {
585         @Override
586         public PendingIntent createFromParcel(Parcel in) {
587           return readPendingIntentOrNullFromParcel(in);
588         }
589 
590         @Override
591         public PendingIntent[] newArray(int size) {
592           return new PendingIntent[size];
593         }
594       };
595 
596   @Implementation
writeToParcel(Parcel out, int flags)597   protected void writeToParcel(Parcel out, int flags) {
598     writePendingIntentOrNullToParcel(realPendingIntent, out);
599   }
600 
create( Context context, Intent[] intents, Type type, int requestCode, int flags, @Nullable Bundle options)601   private static PendingIntent create(
602       Context context,
603       Intent[] intents,
604       Type type,
605       int requestCode,
606       int flags,
607       @Nullable Bundle options) {
608     synchronized (lock) {
609       Objects.requireNonNull(intents, "intents may not be null");
610 
611       // Search for a matching PendingIntent.
612       PendingIntent pendingIntent = getCreatedIntentFor(type, intents, requestCode, flags);
613       if ((flags & FLAG_NO_CREATE) != 0) {
614         return pendingIntent;
615       }
616 
617       // If requested, update the existing PendingIntent if one exists.
618       if (pendingIntent != null && (flags & FLAG_UPDATE_CURRENT) != 0) {
619         ShadowPendingIntent shadowPendingIntent = Shadow.extract(pendingIntent);
620         Intent intent = shadowPendingIntent.getSavedIntent();
621         Bundle extras = intent.getExtras();
622         if (extras != null) {
623           extras.clear();
624         }
625         intent.putExtras(intents[intents.length - 1]);
626         return pendingIntent;
627       }
628 
629       // If requested, cancel the existing PendingIntent if one exists.
630       if (pendingIntent != null && (flags & FLAG_CANCEL_CURRENT) != 0) {
631         ShadowPendingIntent shadowPendingIntent = Shadow.extract(pendingIntent);
632         shadowPendingIntent.cancel();
633         pendingIntent = null;
634       }
635 
636       // Build the PendingIntent if it does not exist.
637       if (pendingIntent == null) {
638         pendingIntent = ReflectionHelpers.callConstructor(PendingIntent.class);
639         // Some methods (e.g. toString) may NPE if 'mTarget' is null.
640         reflector(PendingIntentReflector.class, pendingIntent)
641             .setTarget(ReflectionHelpers.createNullProxy(IIntentSender.class));
642         ShadowPendingIntent shadowPendingIntent = Shadow.extract(pendingIntent);
643         shadowPendingIntent.savedIntents = intents;
644         shadowPendingIntent.type = type;
645         shadowPendingIntent.savedContext = context;
646         shadowPendingIntent.requestCode = requestCode;
647         shadowPendingIntent.flags = flags;
648         shadowPendingIntent.options = options;
649 
650         createdIntents.add(pendingIntent);
651       }
652 
653       return pendingIntent;
654     }
655   }
656 
getCreatedIntentFor( Type type, Intent[] intents, int requestCode, int flags)657   private static PendingIntent getCreatedIntentFor(
658       Type type, Intent[] intents, int requestCode, int flags) {
659     synchronized (lock) {
660       for (PendingIntent createdIntent : createdIntents) {
661         ShadowPendingIntent shadowPendingIntent = Shadow.extract(createdIntent);
662 
663         if (isOneShot(shadowPendingIntent.flags) != isOneShot(flags)) {
664           continue;
665         }
666 
667         if (isMutable(shadowPendingIntent.flags) != isMutable(flags)) {
668           continue;
669         }
670 
671         if (shadowPendingIntent.type != type) {
672           continue;
673         }
674 
675         if (shadowPendingIntent.requestCode != requestCode) {
676           continue;
677         }
678 
679         // The last Intent in the array acts as the "significant element" for matching as per
680         // {@link #getActivities(Context, int, Intent[], int)}.
681         Intent savedIntent = shadowPendingIntent.getSavedIntent();
682         Intent targetIntent = intents[intents.length - 1];
683 
684         if (savedIntent == null ? targetIntent == null : savedIntent.filterEquals(targetIntent)) {
685           return createdIntent;
686         }
687       }
688       return null;
689     }
690   }
691 
isOneShot(int flags)692   private static boolean isOneShot(int flags) {
693     return (flags & FLAG_ONE_SHOT) != 0;
694   }
695 
isMutable(int flags)696   private static boolean isMutable(int flags) {
697     return (flags & FLAG_IMMUTABLE) == 0;
698   }
699 
700   @Resetter
reset()701   public static void reset() {
702     synchronized (lock) {
703       createdIntents.clear();
704       parceledPendingIntents.clear();
705     }
706   }
707 
708   @ForType(PendingIntent.class)
709   interface PendingIntentReflector {
710     @Accessor("mTarget")
setTarget(IIntentSender target)711     void setTarget(IIntentSender target);
712   }
713 }
714