Merge changes from topic "expose-pending-intent" into stage-aosp-master am: b4f626d13e am: 78f1a9bf51
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15879511 Change-Id: I66a50ffbae15636a6a2221f1b27b2449b724a26b
This commit is contained in:
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user