diff options
| -rw-r--r-- | libs/binder/rust/src/parcel.rs | 92 |
1 files changed, 89 insertions, 3 deletions
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs index ef84ade585..a3f7620474 100644 --- a/libs/binder/rust/src/parcel.rs +++ b/libs/binder/rust/src/parcel.rs @@ -184,11 +184,17 @@ impl Parcel { } } + /// 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 @@ impl Parcel { 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 @@ impl Parcel { } } +/// 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<()> { |