summaryrefslogtreecommitdiff
path: root/core/java/android
diff options
context:
space:
mode:
authorMakoto Onuki <omakoto@google.com>2021-09-17 11:27:43 -0700
committerEtienne Ruffieux <eruffieux@google.com>2021-09-27 09:35:58 +0000
commit41b8d147fd82ab6d21acd33040328b61d6af6e11 (patch)
tree7ba89ac09691e18191acb0ba07b665b090d710e2 /core/java/android
parent30bd1bb72d3de0efe3a2b460b927c52fc5b0f742 (diff)
Reduce PendingIntent memory allocation
Fix: 195146423 Test: atest android.app.cts.PendingIntentTest Change-Id: I246d272c77337f77623f0c11d5f445895cdbf5e8
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/app/PendingIntent.java98
1 files changed, 47 insertions, 51 deletions
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 086350084439..9b9eed46db02 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -127,33 +127,36 @@ import java.util.concurrent.Executor;
*/
public final class PendingIntent implements Parcelable {
private static final String TAG = "PendingIntent";
+ @NonNull
private final IIntentSender mTarget;
- private IResultReceiver mCancelReceiver;
private IBinder mWhitelistToken;
+ // cached pending intent information
+ private @Nullable PendingIntentInfo mCachedInfo;
+
/**
- * 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.
+ * 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 Object mLock = new Object();
+ private final class CancelListerInfo extends IResultReceiver.Stub {
+ private final ArraySet<Pair<Executor, CancelListener>> mCancelListeners = new ArraySet<>();
- @GuardedBy("mLock")
- private ArraySet<Pair<Executor, CancelListener>> mCancelListeners;
+ /**
+ * 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;
- /**
- * 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;
+ @Override
+ public void send(int resultCode, Bundle resultData) throws RemoteException {
+ notifyCancelListeners();
+ }
+ }
- // cached pending intent information
- private @Nullable PendingIntentInfo mCachedInfo;
+ @GuardedBy("mTarget")
+ private @Nullable CancelListerInfo mCancelListerInfo;
/**
* It is now required to specify either {@link #FLAG_IMMUTABLE}
@@ -1094,51 +1097,43 @@ public final class PendingIntent implements Parcelable {
@TestApi
public boolean addCancelListener(@NonNull Executor executor,
@NonNull CancelListener cancelListener) {
- synchronized (mLock) {
- if (mCanceled) {
+ synchronized (mTarget) {
+ if (mCancelListerInfo != null && mCancelListerInfo.mCanceled) {
return false;
}
-
- if (mCancelReceiver == null) {
- mCancelReceiver = new IResultReceiver.Stub() {
- @Override
- public void send(int resultCode, Bundle resultData) {
- notifyCancelListeners();
- }
- };
- }
- if (mCancelListeners == null) {
- mCancelListeners = new ArraySet<>();
+ if (mCancelListerInfo == null) {
+ mCancelListerInfo = new CancelListerInfo();
}
- boolean wasEmpty = mCancelListeners.isEmpty();
- mCancelListeners.add(Pair.create(executor, cancelListener));
+ final CancelListerInfo cli = mCancelListerInfo;
+
+ boolean wasEmpty = cli.mCancelListeners.isEmpty();
+ cli.mCancelListeners.add(Pair.create(executor, cancelListener));
if (wasEmpty) {
boolean success;
try {
success = ActivityManager.getService().registerIntentSenderCancelListenerEx(
- mTarget, mCancelReceiver);
+ mTarget, cli);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
if (!success) {
- mCanceled = true;
+ cli.mCanceled = true;
}
return success;
} else {
- return !mCanceled;
+ return !cli.mCanceled;
}
}
}
private void notifyCancelListeners() {
ArraySet<Pair<Executor, CancelListener>> cancelListeners;
- synchronized (mLock) {
- if (mCancelListeners == null || mCancelListeners.size() == 0) {
- return;
- }
- mCanceled = true;
- cancelListeners = new ArraySet<>(mCancelListeners);
- mCancelListeners.clear();
+ synchronized (mTarget) {
+ // When notifyCancelListeners() is called, mCancelListerInfo must always be non-null.
+ final CancelListerInfo cli = mCancelListerInfo;
+ cli.mCanceled = true;
+ cancelListeners = new ArraySet<>(cli.mCancelListeners);
+ cli.mCancelListeners.clear();
}
int size = cancelListeners.size();
for (int i = 0; i < size; i++) {
@@ -1164,19 +1159,20 @@ public final class PendingIntent implements Parcelable {
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public void removeCancelListener(@NonNull CancelListener cancelListener) {
- synchronized (mLock) {
- if (mCancelListeners.size() == 0) {
+ synchronized (mTarget) {
+ final CancelListerInfo cli = mCancelListerInfo;
+ if (cli == null || cli.mCancelListeners.size() == 0) {
return;
}
- for (int i = mCancelListeners.size() - 1; i >= 0; i--) {
- if (mCancelListeners.valueAt(i).second == cancelListener) {
- mCancelListeners.removeAt(i);
+ for (int i = cli.mCancelListeners.size() - 1; i >= 0; i--) {
+ if (cli.mCancelListeners.valueAt(i).second == cancelListener) {
+ cli.mCancelListeners.removeAt(i);
}
}
- if (mCancelListeners.isEmpty()) {
+ if (cli.mCancelListeners.isEmpty()) {
try {
ActivityManager.getService().unregisterIntentSenderCancelListener(mTarget,
- mCancelReceiver);
+ cli);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}