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