diff options
| author | Makoto Onuki <omakoto@google.com> | 2021-09-17 11:27:43 -0700 |
|---|---|---|
| committer | Etienne Ruffieux <eruffieux@google.com> | 2021-09-27 09:35:58 +0000 |
| commit | 41b8d147fd82ab6d21acd33040328b61d6af6e11 (patch) | |
| tree | 7ba89ac09691e18191acb0ba07b665b090d710e2 /core/java/android | |
| parent | 30bd1bb72d3de0efe3a2b460b927c52fc5b0f742 (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.java | 98 |
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(); } |
