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