binder_rs: Add sized_read to Parcel
Add a new Parcel::sized_read method that builds
a sized readable sub-parcel with an end position and is
used by the AIDL compiler to deserialize parcelables.
Bug: 186724059
Test: atest aidl_integration_test
Change-Id: Iba064858fe8b434e89732d437ccc304bc6cd172a
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index ef84ade..a3f7620 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -184,11 +184,17 @@
}
}
+ /// Returns the total size of the parcel.
+ pub fn get_data_size(&self) -> i32 {
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an `AParcel`,
+ // and this call is otherwise safe.
+ sys::AParcel_getDataSize(self.as_native())
+ }
+ }
+
/// Move the current read/write position in the parcel.
///
- /// The new position must be a position previously returned by
- /// `self.get_data_position()`.
- ///
/// # Safety
///
/// This method is safe if `pos` is less than the current size of the parcel
@@ -226,6 +232,65 @@
x.deserialize_from(self)
}
+ /// Safely read a sized parcelable.
+ ///
+ /// Read the size of a parcelable, compute the end position
+ /// of that parcelable, then build a sized readable sub-parcel
+ /// and call a closure with the sub-parcel as its parameter.
+ /// The closure can keep reading data from the sub-parcel
+ /// until it runs out of input data. The closure is responsible
+ /// for calling [`ReadableSubParcel::has_more_data`] to check for
+ /// more data before every read, at least until Rust generators
+ /// are stabilized.
+ /// After the closure returns, skip to the end of the current
+ /// parcelable regardless of how much the closure has read.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// let mut parcelable = Default::default();
+ /// parcel.sized_read(|subparcel| {
+ /// if subparcel.has_more_data() {
+ /// parcelable.a = subparcel.read()?;
+ /// }
+ /// if subparcel.has_more_data() {
+ /// parcelable.b = subparcel.read()?;
+ /// }
+ /// Ok(())
+ /// });
+ /// ```
+ ///
+ pub fn sized_read<F>(&self, mut f: F) -> Result<()>
+ where
+ for<'a> F: FnMut(ReadableSubParcel<'a>) -> Result<()>
+ {
+ let start = self.get_data_position();
+ let parcelable_size: i32 = self.read()?;
+ if parcelable_size < 0 {
+ return Err(StatusCode::BAD_VALUE);
+ }
+
+ let end = start.checked_add(parcelable_size)
+ .ok_or(StatusCode::BAD_VALUE)?;
+ if end > self.get_data_size() {
+ return Err(StatusCode::NOT_ENOUGH_DATA);
+ }
+
+ let subparcel = ReadableSubParcel {
+ parcel: self,
+ end_position: end,
+ };
+ f(subparcel)?;
+
+ // Advance the data position to the actual end,
+ // in case the closure read less data than was available
+ unsafe {
+ self.set_data_position(end)?;
+ }
+
+ Ok(())
+ }
+
/// Read a vector size from the `Parcel` and resize the given output vector
/// to be correctly sized for that amount of data.
///
@@ -271,6 +336,27 @@
}
}
+/// A segment of a readable parcel, used for [`Parcel::sized_read`].
+pub struct ReadableSubParcel<'a> {
+ parcel: &'a Parcel,
+ end_position: i32,
+}
+
+impl<'a> ReadableSubParcel<'a> {
+ /// Read a type that implements [`Deserialize`] from the sub-parcel.
+ pub fn read<D: Deserialize>(&self) -> Result<D> {
+ // The caller should have checked this,
+ // but it can't hurt to double-check
+ assert!(self.has_more_data());
+ D::deserialize(self.parcel)
+ }
+
+ /// Check if the sub-parcel has more data to read
+ pub fn has_more_data(&self) -> bool {
+ self.parcel.get_data_position() < self.end_position
+ }
+}
+
// Internal APIs
impl Parcel {
pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> {