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