diff --git a/core/java/android/content/pm/BaseParceledListSlice.java b/core/java/android/content/pm/BaseParceledListSlice.java index 1e0deffbf8cd4..37b17788c9cc0 100644 --- a/core/java/android/content/pm/BaseParceledListSlice.java +++ b/core/java/android/content/pm/BaseParceledListSlice.java @@ -17,6 +17,7 @@ package android.content.pm; import android.compat.annotation.UnsupportedAppUsage; +import android.os.BadParcelableException; import android.os.Binder; import android.os.Build; import android.os.IBinder; @@ -92,18 +93,20 @@ abstract class BaseParceledListSlice implements Parcelable { data.writeInt(i); try { retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); + reply.readException(); + while (i < N && reply.readInt() != 0) { + listElementClass = readVerifyAndAddElement(creator, reply, loader, + listElementClass); + if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1)); + i++; + } } catch (RemoteException e) { - Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e); - return; + throw new BadParcelableException( + "Failure retrieving array; only received " + i + " of " + N, e); + } finally { + reply.recycle(); + data.recycle(); } - while (i < N && reply.readInt() != 0) { - listElementClass = readVerifyAndAddElement(creator, reply, loader, - listElementClass); - if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1)); - i++; - } - reply.recycle(); - data.recycle(); } } @@ -201,22 +204,29 @@ abstract class BaseParceledListSlice implements Parcelable { + Binder.getCallingPid() + ", sender=" + this); } - while (i < N && reply.dataSize() < MAX_IPC_SIZE) { - reply.writeInt(1); + try { + reply.writeNoException(); + while (i < N && reply.dataSize() < MAX_IPC_SIZE) { + reply.writeInt(1); - final T parcelable = mList.get(i); - verifySameType(listElementClass, parcelable.getClass()); - writeElement(parcelable, reply, callFlags); + final T parcelable = mList.get(i); + verifySameType(listElementClass, parcelable.getClass()); + writeElement(parcelable, reply, callFlags); - if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i)); - i++; - } - 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"); + if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i)); + i++; + } + if (i < N) { + if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N); + reply.writeInt(0); + } else { + if (DEBUG) Log.d(TAG, "Transfer done, clearing mList reference"); + mList = null; + } + } catch (RuntimeException e) { + if (DEBUG) Log.d(TAG, "Transfer failed, clearing mList reference"); mList = null; + throw e; } return true; } diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java index 01907fb099270..dea1b0e4a632a 100644 --- a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java +++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java @@ -16,8 +16,11 @@ package android.content.pm; +import static org.junit.Assert.assertThrows; + import android.os.Parcel; import android.os.Parcelable; +import android.os.ServiceSpecificException; import android.platform.test.annotations.Presubmit; import androidx.test.filters.LargeTest; @@ -114,6 +117,34 @@ public class ParceledListSliceTest extends TestCase { } } + /** + * Test that exceptions created when parcelling data in the service are really + * sent to the client and re-thrown. + */ + public void testThrownException() throws Exception { + final List throwers = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + throwers.add(new ThrowingObject(/* throws= */ false)); + } + throwers.add(new ThrowingObject(/* throws= */ true)); + + final ParceledListSlice src = new ParceledListSlice<>(throwers); + src.setInlineCountLimit(1); + + Parcel parcel = Parcel.obtain(); + try { + parcel.writeParcelable(src, 0); + parcel.setDataPosition(0); + + assertThrows(ServiceSpecificException.class, () -> { + final ParceledListSlice dst = + parcel.readParcelable(getClass().getClassLoader()); + }); + } finally { + parcel.recycle(); + } + } + private void sendParcelStringList(List list) { StringParceledListSlice slice; Parcel parcel = Parcel.obtain(); @@ -236,6 +267,40 @@ public class ParceledListSliceTest extends TestCase { }; } + public static class ThrowingObject implements Parcelable { + + private final boolean mShouldThrow; + + public ThrowingObject(boolean shouldThrow) { + mShouldThrow = shouldThrow; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (mShouldThrow) { + throw new ServiceSpecificException(1234); + } + dest.writeBoolean(mShouldThrow); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public ThrowingObject createFromParcel(Parcel source) { + return new ThrowingObject(source.readBoolean()); + } + + @Override + public ThrowingObject[] newArray(int size) { + return new ThrowingObject[size]; + } + }; + } + public static class SmallObject extends BaseObject { public int mFieldA; public int mFieldB;