From 122a8ec26056cca0d372884fe629030cba2e6d67 Mon Sep 17 00:00:00 2001 From: Tim Murray Date: Sun, 12 Dec 2021 12:12:09 -0800 Subject: BaseParceledListSlice: remove List reference once transfer completes Currently, BaseParceledListSlice will hold a reference to the List<> until the Binder object is destroyed. However, this requires that the receiver run GC before the sender's global Binder reference can be removed, causing the List to be retained until both receiver and sender perform GC. Since the List is usually the large part of the object, instead clear the reference to the List once the transfer to the receiver completes, allowing the List to be GC'd before the receiver GC's its reference to the sender's Binder object. Test: boots Bug: 213236807 Change-Id: I22d6e56c953db3c85179911b909d5b6e7c8ba784 --- .../android/content/pm/BaseParceledListSlice.java | 25 ++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'core/java/android') diff --git a/core/java/android/content/pm/BaseParceledListSlice.java b/core/java/android/content/pm/BaseParceledListSlice.java index 7bade740c9be..1e0deffbf8cd 100644 --- a/core/java/android/content/pm/BaseParceledListSlice.java +++ b/core/java/android/content/pm/BaseParceledListSlice.java @@ -50,10 +50,12 @@ abstract class BaseParceledListSlice implements Parcelable { */ private static final int MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes(); - private final List mList; + private List mList; private int mInlineCountLimit = Integer.MAX_VALUE; + private boolean mHasBeenParceled = false; + public BaseParceledListSlice(List list) { mList = list; } @@ -151,9 +153,17 @@ abstract class BaseParceledListSlice implements Parcelable { * Write this to another Parcel. Note that this discards the internal Parcel * and should not be used anymore. This is so we can pass this to a Binder * where we won't have a chance to call recycle on this. + * + * This method can only be called once per BaseParceledListSlice to ensure that + * the referenced list can be cleaned up before the recipient cleans up the + * Binder reference. */ @Override public void writeToParcel(Parcel dest, int flags) { + if (mHasBeenParceled) { + throw new IllegalStateException("Can't Parcel a ParceledListSlice more than once"); + } + mHasBeenParceled = true; final int N = mList.size(); final int callFlags = flags; dest.writeInt(N); @@ -180,9 +190,17 @@ abstract class BaseParceledListSlice implements Parcelable { throws RemoteException { if (code != FIRST_CALL_TRANSACTION) { return super.onTransact(code, data, reply, flags); + } else if (mList == null) { + throw new IllegalArgumentException("Attempt to transfer null list, " + + "did transfer finish?"); } int i = data.readInt(); - if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N); + + if (DEBUG) { + Log.d(TAG, "Writing more @" + i + " of " + N + " to " + + Binder.getCallingPid() + ", sender=" + this); + } + while (i < N && reply.dataSize() < MAX_IPC_SIZE) { reply.writeInt(1); @@ -196,6 +214,9 @@ abstract class BaseParceledListSlice implements Parcelable { if (i < N) { if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N); reply.writeInt(0); + } else { + if (DEBUG) Log.d(TAG, "Transfer complete, clearing mList reference"); + mList = null; } return true; } -- cgit v1.2.3