Merge "Ensure ParcelableCall instances are deep copied for local binders." into main
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 3f8f579..6cfa4fd 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -44,6 +44,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.PackageTagsList;
+import android.os.Parcel;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -1723,7 +1724,8 @@
try {
inCallService.updateCall(
- sanitizeParcelableCallForService(info, parcelableCall));
+ copyIfLocal(sanitizeParcelableCallForService(info, parcelableCall),
+ inCallService));
} catch (RemoteException ignored) {
}
}
@@ -2854,7 +2856,8 @@
ParcelableCall parcelableCall, ComponentName componentName) {
try {
inCallService.updateCall(
- sanitizeParcelableCallForService(info, parcelableCall));
+ copyIfLocal(sanitizeParcelableCallForService(info, parcelableCall),
+ inCallService));
} catch (RemoteException exception) {
Log.w(this, "Call status update did not send to: "
+ componentName + " successfully with error " + exception);
@@ -3435,4 +3438,43 @@
}
return false;
}
+
+ /**
+ * Given a {@link ParcelableCall} and a {@link IInCallService}, determines if the ICS binder is
+ * local or remote. If the binder is remote, we just return the parcelable call instance
+ * already constructed.
+ * If the binder if local, as will be the case for
+ * {@code EnhancedConfirmationCallTrackerService} (or any other ICS in the system server, the
+ * underlying Binder implementation is NOT going to parcel and unparcel the
+ * {@link ParcelableCall} instance automatically. This means that the parcelable call instance
+ * is passed by reference and that the ICS in the system server could potentially try to access
+ * internals in the {@link ParcelableCall} in an unsafe manner. As a workaround, we will
+ * manually parcel and unparcel the {@link ParcelableCall} instance so that they get a fresh
+ * copy that they can use safely.
+ *
+ * @param parcelableCall The ParcelableCall instance we want to maybe copy.
+ * @param remote the binder the call is going out over.
+ * @return either the original {@link ParcelableCall} or a deep copy of it if the destination
+ * binder is local.
+ */
+ private ParcelableCall copyIfLocal(ParcelableCall parcelableCall, IInCallService remote) {
+ // We care more about parceling than local (though they should be the same); so, use
+ // queryLocalInterface since that's what Binder uses to decide if it needs to parcel.
+ if (remote.asBinder().queryLocalInterface(IInCallService.Stub.DESCRIPTOR) == null) {
+ // No local interface, so binder itself will parcel and thus we don't need to.
+ return parcelableCall;
+ }
+ // Binder won't be parceling; however, the remotes assume they have their own native
+ // objects (and don't know if caller is local or not), so we need to make a COPY here so
+ // that the remote can clean it up without clearing the original transaction.
+ // Since there's no direct `copy` for Transaction, we have to parcel/unparcel instead.
+ final Parcel p = Parcel.obtain();
+ try {
+ parcelableCall.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ return ParcelableCall.CREATOR.createFromParcel(p);
+ } finally {
+ p.recycle();
+ }
+ }
}