Merge changes from topic "expose-pending-intent" into stage-aosp-master am: b4f626d13e am: 78f1a9bf51 am: 4230a055d3

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15879511

Change-Id: I1f9814117187d712c4e5df8140d6ff62f06ebdf3
This commit is contained in:
TreeHugger Robot
2021-09-28 11:34:24 +00:00
committed by Automerger Merge Worker

View File

@@ -127,34 +127,37 @@ import java.util.concurrent.Executor;
*/ */
public final class PendingIntent implements Parcelable { public final class PendingIntent implements Parcelable {
private static final String TAG = "PendingIntent"; private static final String TAG = "PendingIntent";
@NonNull
private final IIntentSender mTarget; private final IIntentSender mTarget;
private IResultReceiver mCancelReceiver;
private IBinder mWhitelistToken; private IBinder mWhitelistToken;
/**
* To protect {@link #mCancelListeners}. We could stop lazy-initialization and synchronize
* on {@link #mCancelListeners} directly, and that wouldn't increase allocations
* (an empty ArraySet won't causew extra allocations), but
* because an empty ArraySet is slightly larger than an Object, and because
* {@link #addCancelListener} is rarely used, having a separate lock object would probably
* be a net win.
*/
private final Object mLock = new Object();
@GuardedBy("mLock")
private ArraySet<Pair<Executor, CancelListener>> mCancelListeners;
/**
* Whether the PI is canceld or not. Note this is essentially a "cache" that's updated
* only when the client uses {@link #addCancelListener}. Even if this is fase, that
* still doesn't know the PI is *not* cancled, but if it's true, this PI is definitely canceled.
*/
@GuardedBy("mLock")
private boolean mCanceled;
// cached pending intent information // cached pending intent information
private @Nullable PendingIntentInfo mCachedInfo; private @Nullable PendingIntentInfo mCachedInfo;
/**
* Structure to store information related to {@link #addCancelListener}, which is rarely used,
* so we lazily allocate it to keep the PendingIntent class size small.
*/
private final class CancelListerInfo extends IResultReceiver.Stub {
private final ArraySet<Pair<Executor, CancelListener>> mCancelListeners = new ArraySet<>();
/**
* Whether the PI is canceled or not. Note this is essentially a "cache" that's updated
* only when the client uses {@link #addCancelListener}. Even if this is false, that
* still doesn't know the PI is *not* canceled, but if it's true, this PI is definitely
* canceled.
*/
private boolean mCanceled;
@Override
public void send(int resultCode, Bundle resultData) throws RemoteException {
notifyCancelListeners();
}
}
@GuardedBy("mTarget")
private @Nullable CancelListerInfo mCancelListerInfo;
/** /**
* It is now required to specify either {@link #FLAG_IMMUTABLE} * It is now required to specify either {@link #FLAG_IMMUTABLE}
* or {@link #FLAG_MUTABLE} when creating a PendingIntent. * or {@link #FLAG_MUTABLE} when creating a PendingIntent.
@@ -1094,51 +1097,43 @@ public final class PendingIntent implements Parcelable {
@TestApi @TestApi
public boolean addCancelListener(@NonNull Executor executor, public boolean addCancelListener(@NonNull Executor executor,
@NonNull CancelListener cancelListener) { @NonNull CancelListener cancelListener) {
synchronized (mLock) { synchronized (mTarget) {
if (mCanceled) { if (mCancelListerInfo != null && mCancelListerInfo.mCanceled) {
return false; return false;
} }
if (mCancelListerInfo == null) {
mCancelListerInfo = new CancelListerInfo();
}
final CancelListerInfo cli = mCancelListerInfo;
if (mCancelReceiver == null) { boolean wasEmpty = cli.mCancelListeners.isEmpty();
mCancelReceiver = new IResultReceiver.Stub() { cli.mCancelListeners.add(Pair.create(executor, cancelListener));
@Override
public void send(int resultCode, Bundle resultData) {
notifyCancelListeners();
}
};
}
if (mCancelListeners == null) {
mCancelListeners = new ArraySet<>();
}
boolean wasEmpty = mCancelListeners.isEmpty();
mCancelListeners.add(Pair.create(executor, cancelListener));
if (wasEmpty) { if (wasEmpty) {
boolean success; boolean success;
try { try {
success = ActivityManager.getService().registerIntentSenderCancelListenerEx( success = ActivityManager.getService().registerIntentSenderCancelListenerEx(
mTarget, mCancelReceiver); mTarget, cli);
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }
if (!success) { if (!success) {
mCanceled = true; cli.mCanceled = true;
} }
return success; return success;
} else { } else {
return !mCanceled; return !cli.mCanceled;
} }
} }
} }
private void notifyCancelListeners() { private void notifyCancelListeners() {
ArraySet<Pair<Executor, CancelListener>> cancelListeners; ArraySet<Pair<Executor, CancelListener>> cancelListeners;
synchronized (mLock) { synchronized (mTarget) {
if (mCancelListeners == null || mCancelListeners.size() == 0) { // When notifyCancelListeners() is called, mCancelListerInfo must always be non-null.
return; final CancelListerInfo cli = mCancelListerInfo;
} cli.mCanceled = true;
mCanceled = true; cancelListeners = new ArraySet<>(cli.mCancelListeners);
cancelListeners = new ArraySet<>(mCancelListeners); cli.mCancelListeners.clear();
mCancelListeners.clear();
} }
int size = cancelListeners.size(); int size = cancelListeners.size();
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
@@ -1164,19 +1159,20 @@ public final class PendingIntent implements Parcelable {
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@TestApi @TestApi
public void removeCancelListener(@NonNull CancelListener cancelListener) { public void removeCancelListener(@NonNull CancelListener cancelListener) {
synchronized (mLock) { synchronized (mTarget) {
if (mCancelListeners.size() == 0) { final CancelListerInfo cli = mCancelListerInfo;
if (cli == null || cli.mCancelListeners.size() == 0) {
return; return;
} }
for (int i = mCancelListeners.size() - 1; i >= 0; i--) { for (int i = cli.mCancelListeners.size() - 1; i >= 0; i--) {
if (mCancelListeners.valueAt(i).second == cancelListener) { if (cli.mCancelListeners.valueAt(i).second == cancelListener) {
mCancelListeners.removeAt(i); cli.mCancelListeners.removeAt(i);
} }
} }
if (mCancelListeners.isEmpty()) { if (cli.mCancelListeners.isEmpty()) {
try { try {
ActivityManager.getService().unregisterIntentSenderCancelListener(mTarget, ActivityManager.getService().unregisterIntentSenderCancelListener(mTarget,
mCancelReceiver); cli);
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }