Verify that remote exceptions will not cause object leaks

Test: atest CtsOsTestCases:ParcelTest#testExceptionOverwritesObject
Bug: 34175893
Change-Id: Idef19c1413a992ccd58c47b3ca1449aad42a8a0a
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index aca23e3..b7e5f09 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -41,7 +41,8 @@
     src/android/os/cts/IEmptyService.aidl \
     src/android/os/cts/ISeccompIsolatedService.aidl \
     src/android/os/cts/ISecondary.aidl \
-    src/android/os/cts/ISharedMemoryService.aidl
+    src/android/os/cts/ISharedMemoryService.aidl \
+    src/android/os/cts/IParcelExceptionService.aidl
 
 LOCAL_PACKAGE_NAME := CtsOsTestCases
 
diff --git a/tests/tests/os/AndroidManifest.xml b/tests/tests/os/AndroidManifest.xml
index 4092538..d706470 100644
--- a/tests/tests/os/AndroidManifest.xml
+++ b/tests/tests/os/AndroidManifest.xml
@@ -84,6 +84,10 @@
             android:name="android.os.cts.SharedMemoryService"
             android:process=":sharedmem"
             android:exported="false" />
+        <service
+            android:name="android.os.cts.ParcelExceptionService"
+            android:process=":remote"
+            android:exported="true" />
 
         <service android:name="android.os.cts.LocalService">
             <intent-filter>
diff --git a/tests/tests/os/src/android/os/cts/ExceptionalParcelable.aidl b/tests/tests/os/src/android/os/cts/ExceptionalParcelable.aidl
new file mode 100644
index 0000000..7d09693
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/ExceptionalParcelable.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.cts;
+
+parcelable ExceptionalParcelable;
\ No newline at end of file
diff --git a/tests/tests/os/src/android/os/cts/ExceptionalParcelable.java b/tests/tests/os/src/android/os/cts/ExceptionalParcelable.java
new file mode 100644
index 0000000..333cf57
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/ExceptionalParcelable.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.os.cts;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+public class ExceptionalParcelable implements Parcelable {
+    private final IBinder mBinder;
+
+    ExceptionalParcelable(IBinder binder) {
+        mBinder = binder;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Write a binder to the Parcel and then throw an exception
+     */
+    public void writeToParcel(Parcel out, int flags) {
+        // Write a binder for the exception to overwrite
+        out.writeStrongBinder(mBinder);
+
+        // Throw an exception
+        throw new IllegalArgumentException("A truly exceptional message");
+    }
+
+    public static final Creator<ExceptionalParcelable> CREATOR =
+            new Creator<ExceptionalParcelable>() {
+                @Override
+                public ExceptionalParcelable createFromParcel(Parcel source) {
+                    return new ExceptionalParcelable(source.readStrongBinder());
+                }
+
+                @Override
+                public ExceptionalParcelable[] newArray(int size) {
+                    return new ExceptionalParcelable[size];
+                }
+            };
+}
diff --git a/tests/tests/os/src/android/os/cts/IParcelExceptionService.aidl b/tests/tests/os/src/android/os/cts/IParcelExceptionService.aidl
new file mode 100644
index 0000000..ce7af6d
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/IParcelExceptionService.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.cts;
+import android.os.cts.ExceptionalParcelable;
+
+interface IParcelExceptionService {
+//parcelable android.os.cts.ExceptionalParcelable;
+    ExceptionalParcelable writeBinderThrowException();
+}
diff --git a/tests/tests/os/src/android/os/cts/ParcelExceptionService.java b/tests/tests/os/src/android/os/cts/ParcelExceptionService.java
new file mode 100644
index 0000000..d8387e3
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/ParcelExceptionService.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.os.cts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+public class ParcelExceptionService extends Service {
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new ParcelExceptionServiceImpl();
+    }
+
+    private static class ParcelExceptionServiceImpl extends IParcelExceptionService.Stub {
+        private final IBinder mBinder = new Binder();
+
+
+        @Override
+        public ExceptionalParcelable writeBinderThrowException() throws RemoteException {
+            return new ExceptionalParcelable(mBinder);
+        }
+    }
+}
diff --git a/tests/tests/os/src/android/os/cts/ParcelTest.java b/tests/tests/os/src/android/os/cts/ParcelTest.java
index 3715850..77d325c 100644
--- a/tests/tests/os/src/android/os/cts/ParcelTest.java
+++ b/tests/tests/os/src/android/os/cts/ParcelTest.java
@@ -24,7 +24,14 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.Signature;
 import android.os.BadParcelableException;
 import android.os.Binder;
@@ -39,6 +46,8 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 
+import com.google.common.util.concurrent.AbstractFuture;
+
 public class ParcelTest extends AndroidTestCase {
 
     public void testObtain() {
@@ -3308,4 +3317,55 @@
             // good
         }
     }
+
+    public static class ParcelExceptionConnection extends AbstractFuture<IParcelExceptionService>
+            implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            set(IParcelExceptionService.Stub.asInterface(service));
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+        }
+
+        @Override
+        public IParcelExceptionService get() throws InterruptedException, ExecutionException {
+            try {
+                return get(5, TimeUnit.SECONDS);
+            } catch (TimeoutException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public void testExceptionOverwritesObject() throws Exception {
+        final Intent intent = new Intent();
+        intent.setComponent(new ComponentName(
+                "android.os.cts", "android.os.cts.ParcelExceptionService"));
+
+        final ParcelExceptionConnection connection = new ParcelExceptionConnection();
+
+        mContext.startService(intent);
+        assertTrue(mContext.bindService(intent, connection,
+                Context.BIND_ABOVE_CLIENT | Context.BIND_EXTERNAL_SERVICE));
+
+
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken("android.os.cts.IParcelExceptionService");
+        IParcelExceptionService service = connection.get();
+        try {
+            assertTrue("Transaction failed", service.asBinder().transact(
+                    IParcelExceptionService.Stub.TRANSACTION_writeBinderThrowException, data, reply,
+                    0));
+        } catch (Exception e) {
+            fail("Exception caught from transaction: " + e);
+        }
+        reply.setDataPosition(0);
+        assertTrue("Exception should have occurred on service-side",
+                reply.readExceptionCode() != 0);
+        assertNull("Binder should have been overwritten by the exception",
+                reply.readStrongBinder());
+    }
 }