diff options
| -rw-r--r-- | libs/binder/rust/Android.bp | 2 | ||||
| -rw-r--r-- | libs/binder/rust/src/binder.rs | 24 | ||||
| -rw-r--r-- | libs/binder/rust/src/lib.rs | 2 | ||||
| -rw-r--r-- | libs/binder/rust/src/parcel.rs | 152 | ||||
| -rw-r--r-- | libs/binder/rust/src/parcel/parcelable.rs | 133 | ||||
| -rw-r--r-- | libs/binder/rust/src/parcel/parcelable_holder.rs | 257 |
6 files changed, 490 insertions, 80 deletions
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index d9d7cafa7c..ecb044e589 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -17,6 +17,7 @@ rust_library { rustlibs: [ "liblibc", "libbinder_ndk_sys", + "libdowncast_rs", ], host_supported: true, target: { @@ -133,6 +134,7 @@ rust_test { rustlibs: [ "liblibc", "libbinder_ndk_sys", + "libdowncast_rs", ], } diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 41ceee5697..854b1f9d4e 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -23,6 +23,7 @@ use crate::sys; use std::borrow::Borrow; use std::cmp::Ordering; +use std::convert::TryFrom; use std::ffi::{c_void, CStr, CString}; use std::fmt; use std::fs::File; @@ -70,6 +71,7 @@ pub trait Interface: Send + Sync { /// An interface can promise to be a stable vendor interface ([`Vintf`]), or /// makes no stability guarantees ([`Local`]). [`Local`] is /// currently the default stability. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum Stability { /// Default stability, visible to other modules in the same compilation /// context (e.g. modules on system.img) @@ -85,6 +87,28 @@ impl Default for Stability { } } +impl From<Stability> for i32 { + fn from(stability: Stability) -> i32 { + use Stability::*; + match stability { + Local => 0, + Vintf => 1, + } + } +} + +impl TryFrom<i32> for Stability { + type Error = StatusCode; + fn try_from(stability: i32) -> Result<Stability> { + use Stability::*; + match stability { + 0 => Ok(Local), + 1 => Ok(Vintf), + _ => Err(StatusCode::BAD_VALUE) + } + } +} + /// A local service that can be remotable via Binder. /// /// An object that implement this interface made be made into a Binder service diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 7e8e3a5a5f..d1d37d710f 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -127,7 +127,7 @@ pub mod unstable_api { /// The public API usable outside AIDL-generated interface crates. pub mod public_api { - pub use super::parcel::ParcelFileDescriptor; + pub use super::parcel::{ParcelFileDescriptor, ParcelableHolder}; pub use super::{ add_service, force_lazy_services_persist, get_interface, register_lazy_service, wait_for_interface, diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs index dad89ec97d..73915610d8 100644 --- a/libs/binder/rust/src/parcel.rs +++ b/libs/binder/rust/src/parcel.rs @@ -29,11 +29,14 @@ use std::fmt; mod file_descriptor; mod parcelable; +mod parcelable_holder; pub use self::file_descriptor::ParcelFileDescriptor; pub use self::parcelable::{ Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption, + Parcelable, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG, }; +pub use self::parcelable_holder::{ParcelableHolder, ParcelableMetadata}; /// Container for a message (data and object references) that can be sent /// through Binder. @@ -68,6 +71,21 @@ unsafe impl AsNative<sys::AParcel> for Parcel { } impl Parcel { + /// Create a new empty `Parcel`. + /// + /// Creates a new owned empty parcel that can be written to + /// using the serialization methods and appended to and + /// from using `append_from` and `append_from_all`. + pub fn new() -> Parcel { + let parcel = unsafe { + // Safety: If `AParcel_create` succeeds, it always returns + // a valid pointer. If it fails, the process will crash. + sys::AParcel_create() + }; + assert!(!parcel.is_null()); + Self::Owned(parcel) + } + /// Create a borrowed reference to a parcel object from a raw pointer. /// /// # Safety @@ -106,6 +124,22 @@ impl Parcel { } } +impl Default for Parcel { + fn default() -> Self { + Self::new() + } +} + +impl Clone for Parcel { + fn clone(&self) -> Self { + let mut new_parcel = Self::new(); + new_parcel + .append_all_from(self) + .expect("Failed to append from Parcel"); + new_parcel + } +} + // Data serialization methods impl Parcel { /// Data written to parcelable is zero'd before being deleted or reallocated. @@ -213,6 +247,30 @@ impl Parcel { pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> { status_result(sys::AParcel_setDataPosition(self.as_native(), pos)) } + + /// Append a subset of another `Parcel`. + /// + /// This appends `size` bytes of data from `other` starting at offset + /// `start` to the current `Parcel`, or returns an error if not possible. + pub fn append_from(&mut self, other: &Self, start: i32, size: i32) -> Result<()> { + let status = unsafe { + // Safety: `Parcel::appendFrom` from C++ checks that `start` + // and `size` are in bounds, and returns an error otherwise. + // Both `self` and `other` always contain valid pointers. + sys::AParcel_appendFrom( + other.as_native(), + self.as_native_mut(), + start, + size, + ) + }; + status_result(status) + } + + /// Append the contents of another `Parcel`. + pub fn append_all_from(&mut self, other: &Self) -> Result<()> { + self.append_from(other, 0, other.get_data_size()) + } } /// A segment of a writable parcel, used for [`Parcel::sized_write`]. @@ -427,43 +485,9 @@ impl fmt::Debug for Parcel { } } -#[cfg(test)] -impl Parcel { - /// Create a new parcel tied to a bogus binder. TESTING ONLY! - /// - /// This can only be used for testing! All real parcel operations must be - /// done in the callback to [`IBinder::transact`] or in - /// [`Remotable::on_transact`] using the parcels provided to these methods. - pub(crate) fn new_for_test(binder: &mut SpIBinder) -> Result<Self> { - let mut input = ptr::null_mut(); - let status = unsafe { - // Safety: `SpIBinder` guarantees that `binder` always contains a - // valid pointer to an `AIBinder`. We pass a valid, mutable out - // pointer to receive a newly constructed parcel. When successful - // this function assigns a new pointer to an `AParcel` to `input` - // and transfers ownership of this pointer to the caller. Thus, - // after this call, `input` will either be null or point to a valid, - // owned `AParcel`. - sys::AIBinder_prepareTransaction(binder.as_native_mut(), &mut input) - }; - status_result(status)?; - unsafe { - // Safety: `input` is either null or a valid, owned pointer to an - // `AParcel`, so is valid to safe to - // `Parcel::owned`. `Parcel::owned` takes ownership of the parcel - // pointer. - Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL) - } - } -} - #[test] fn test_read_write() { - use crate::binder::Interface; - use crate::native::Binder; - - let mut service = Binder::new(()).as_binder(); - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let start = parcel.get_data_position(); assert_eq!(parcel.read::<bool>(), Err(StatusCode::NOT_ENOUGH_DATA)); @@ -493,11 +517,7 @@ fn test_read_write() { #[test] #[allow(clippy::float_cmp)] fn test_read_data() { - use crate::binder::Interface; - use crate::native::Binder; - - let mut service = Binder::new(()).as_binder(); - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let str_start = parcel.get_data_position(); parcel.write(&b"Hello, Binder!\0"[..]).unwrap(); @@ -572,11 +592,7 @@ fn test_read_data() { #[test] fn test_utf8_utf16_conversions() { - use crate::binder::Interface; - use crate::native::Binder; - - let mut service = Binder::new(()).as_binder(); - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let start = parcel.get_data_position(); assert!(parcel.write("Hello, Binder!").is_ok()); @@ -636,11 +652,7 @@ fn test_utf8_utf16_conversions() { #[test] fn test_sized_write() { - use crate::binder::Interface; - use crate::native::Binder; - - let mut service = Binder::new(()).as_binder(); - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let start = parcel.get_data_position(); let arr = [1i32, 2i32, 3i32]; @@ -668,3 +680,43 @@ fn test_sized_write() { &arr, ); } + +#[test] +fn test_append_from() { + let mut parcel1 = Parcel::new(); + parcel1.write(&42i32).expect("Could not perform write"); + + let mut parcel2 = Parcel::new(); + assert_eq!(Ok(()), parcel2.append_all_from(&parcel1)); + assert_eq!(4, parcel2.get_data_size()); + assert_eq!(Ok(()), parcel2.append_all_from(&parcel1)); + assert_eq!(8, parcel2.get_data_size()); + unsafe { + parcel2.set_data_position(0).unwrap(); + } + assert_eq!(Ok(42), parcel2.read::<i32>()); + assert_eq!(Ok(42), parcel2.read::<i32>()); + + let mut parcel2 = Parcel::new(); + assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2)); + assert_eq!(Ok(()), parcel2.append_from(&parcel1, 2, 2)); + assert_eq!(4, parcel2.get_data_size()); + unsafe { + parcel2.set_data_position(0).unwrap(); + } + assert_eq!(Ok(42), parcel2.read::<i32>()); + + let mut parcel2 = Parcel::new(); + assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2)); + assert_eq!(2, parcel2.get_data_size()); + unsafe { + parcel2.set_data_position(0).unwrap(); + } + assert_eq!(Err(StatusCode::NOT_ENOUGH_DATA), parcel2.read::<i32>()); + + let mut parcel2 = Parcel::new(); + assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, 4, 2)); + assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, 2, 4)); + assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, -1, 4)); + assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, 2, -1)); +} diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs index 56c61651cf..499ef09e6a 100644 --- a/libs/binder/rust/src/parcel/parcelable.rs +++ b/libs/binder/rust/src/parcel/parcelable.rs @@ -14,19 +14,42 @@ * limitations under the License. */ -use crate::binder::{AsNative, FromIBinder, Strong}; +use crate::binder::{AsNative, FromIBinder, Stability, Strong}; use crate::error::{status_result, status_t, Result, Status, StatusCode}; use crate::parcel::Parcel; use crate::proxy::SpIBinder; use crate::sys; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use std::ffi::c_void; use std::os::raw::{c_char, c_ulong}; use std::mem::{self, MaybeUninit}; use std::ptr; use std::slice; +/// Super-trait for Binder parcelables. +/// +/// This trait is equivalent `android::Parcelable` in C++, +/// and defines a common interface that all parcelables need +/// to implement. +pub trait Parcelable { + /// Internal serialization function for parcelables. + /// + /// This method is mainly for internal use. + /// `Serialize::serialize` and its variants are generally + /// preferred over this function, since the former also + /// prepend a header. + fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()>; + + /// Internal deserialization function for parcelables. + /// + /// This method is mainly for internal use. + /// `Deserialize::deserialize` and its variants are generally + /// preferred over this function, since the former also + /// parse the additional header. + fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()>; +} + /// A struct whose instances can be written to a [`Parcel`]. // Might be able to hook this up as a serde backend in the future? pub trait Serialize { @@ -162,6 +185,18 @@ unsafe extern "C" fn deserialize_element<T: Deserialize>( StatusCode::OK as status_t } +/// Flag that specifies that the following parcelable is present. +/// +/// This is the Rust equivalent of `Parcel::kNonNullParcelableFlag` +/// from `include/binder/Parcel.h` in C++. +pub const NON_NULL_PARCELABLE_FLAG: i32 = 1; + +/// Flag that specifies that the following parcelable is absent. +/// +/// This is the Rust equivalent of `Parcel::kNullParcelableFlag` +/// from `include/binder/Parcel.h` in C++. +pub const NULL_PARCELABLE_FLAG: i32 = 0; + /// Helper trait for types that can be nullable when serialized. // We really need this trait instead of implementing `Serialize for Option<T>` // because of the Rust orphan rule which prevents us from doing @@ -173,10 +208,10 @@ pub trait SerializeOption: Serialize { /// Serialize an Option of this type into the given [`Parcel`]. fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { if let Some(inner) = this { - parcel.write(&1i32)?; + parcel.write(&NON_NULL_PARCELABLE_FLAG)?; parcel.write(inner) } else { - parcel.write(&0i32) + parcel.write(&NULL_PARCELABLE_FLAG) } } } @@ -186,7 +221,7 @@ pub trait DeserializeOption: Deserialize { /// Deserialize an Option of this type from the given [`Parcel`]. fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { let null: i32 = parcel.read()?; - if null == 0 { + if null == NULL_PARCELABLE_FLAG { Ok(None) } else { parcel.read().map(Some) @@ -608,6 +643,18 @@ impl<T: DeserializeArray> DeserializeOption for Vec<T> { } } +impl Serialize for Stability { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + i32::from(*self).serialize(parcel) + } +} + +impl Deserialize for Stability { + fn deserialize(parcel: &Parcel) -> Result<Self> { + i32::deserialize(parcel).and_then(Stability::try_from) + } +} + impl Serialize for Status { fn serialize(&self, parcel: &mut Parcel) -> Result<()> { unsafe { @@ -699,19 +746,53 @@ impl<T: DeserializeOption> Deserialize for Option<T> { } } +/// Implement `Serialize` trait and friends for a parcelable +/// +/// This is an internal macro used by the AIDL compiler to implement +/// `Serialize`, `SerializeArray` and `SerializeOption` for +/// structured parcelables. The target type must implement the +/// `Parcelable` trait. +/// ``` +#[macro_export] +macro_rules! impl_serialize_for_parcelable { + ($parcelable:ident) => { + impl $crate::parcel::Serialize for $parcelable { + fn serialize( + &self, + parcel: &mut $crate::parcel::Parcel, + ) -> $crate::Result<()> { + <Self as $crate::parcel::SerializeOption>::serialize_option( + Some(self), + parcel, + ) + } + } + + impl $crate::parcel::SerializeArray for $parcelable {} + + impl $crate::parcel::SerializeOption for $parcelable { + fn serialize_option( + this: Option<&Self>, + parcel: &mut $crate::parcel::Parcel, + ) -> $crate::Result<()> { + if let Some(this) = this { + use $crate::parcel::Parcelable; + parcel.write(&$crate::parcel::NON_NULL_PARCELABLE_FLAG)?; + this.write_to_parcel(parcel) + } else { + parcel.write(&$crate::parcel::NULL_PARCELABLE_FLAG) + } + } + } + } +} + /// Implement `Deserialize` trait and friends for a parcelable /// /// This is an internal macro used by the AIDL compiler to implement /// `Deserialize`, `DeserializeArray` and `DeserializeOption` for -/// structured parcelables. The target type must implement a -/// `deserialize_parcelable` method with the following signature: -/// ```no_run -/// fn deserialize_parcelable( -/// &mut self, -/// parcel: &binder::parcel::Parcelable, -/// ) -> binder::Result<()> { -/// // ... -/// } +/// structured parcelables. The target type must implement the +/// `Parcelable` trait. /// ``` #[macro_export] macro_rules! impl_deserialize_for_parcelable { @@ -729,10 +810,11 @@ macro_rules! impl_deserialize_for_parcelable { parcel: &$crate::parcel::Parcel, ) -> $crate::Result<()> { let status: i32 = parcel.read()?; - if status == 0 { + if status == $crate::parcel::NULL_PARCELABLE_FLAG { Err($crate::StatusCode::UNEXPECTED_NULL) } else { - self.deserialize_parcelable(parcel) + use $crate::parcel::Parcelable; + self.read_from_parcel(parcel) } } } @@ -752,12 +834,13 @@ macro_rules! impl_deserialize_for_parcelable { parcel: &$crate::parcel::Parcel, ) -> $crate::Result<()> { let status: i32 = parcel.read()?; - if status == 0 { + if status == $crate::parcel::NULL_PARCELABLE_FLAG { *this = None; Ok(()) } else { + use $crate::parcel::Parcelable; this.get_or_insert_with(Self::default) - .deserialize_parcelable(parcel) + .read_from_parcel(parcel) } } } @@ -790,10 +873,6 @@ impl<T: DeserializeOption> DeserializeOption for Box<T> { #[test] fn test_custom_parcelable() { - use crate::binder::Interface; - use crate::native::Binder; - let mut service = Binder::new(()).as_binder(); - struct Custom(u32, bool, String, Vec<String>); impl Serialize for Custom { @@ -826,7 +905,7 @@ fn test_custom_parcelable() { let custom = Custom(123_456_789, true, string8, strs); - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let start = parcel.get_data_position(); assert!(custom.serialize(&mut parcel).is_ok()); @@ -846,13 +925,9 @@ fn test_custom_parcelable() { #[test] #[allow(clippy::excessive_precision)] fn test_slice_parcelables() { - use crate::binder::Interface; - use crate::native::Binder; - let mut service = Binder::new(()).as_binder(); - let bools = [true, false, false, true]; - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let start = parcel.get_data_position(); assert!(bools.serialize(&mut parcel).is_ok()); @@ -876,7 +951,7 @@ fn test_slice_parcelables() { let u8s = [101u8, 255, 42, 117]; - let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let mut parcel = Parcel::new(); let start = parcel.get_data_position(); assert!(parcel.write(&u8s[..]).is_ok()); diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs new file mode 100644 index 0000000000..3e75d1b11d --- /dev/null +++ b/libs/binder/rust/src/parcel/parcelable_holder.rs @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2021 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. + */ + +use crate::binder::Stability; +use crate::error::{Result, StatusCode}; +use crate::parcel::{Parcel, Parcelable}; +use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable}; + +use downcast_rs::{impl_downcast, Downcast}; +use std::any::Any; +use std::cell::RefCell; +use std::rc::Rc; + +/// Metadata that `ParcelableHolder` needs for all parcelables. +/// +/// The compiler auto-generates implementations of this trait +/// for AIDL parcelables. +pub trait ParcelableMetadata { + /// The Binder parcelable descriptor string. + /// + /// This string is a unique identifier for a Binder parcelable. + fn get_descriptor() -> &'static str; + + /// The Binder parcelable stability. + fn get_stability(&self) -> Stability { + Stability::Local + } +} + +trait AnyParcelable: Downcast + Parcelable + std::fmt::Debug {} +impl_downcast!(AnyParcelable); +impl<T> AnyParcelable for T where T: Downcast + Parcelable + std::fmt::Debug {} + +#[derive(Debug, Clone)] +enum ParcelableHolderData { + Empty, + Parcelable { + parcelable: Rc<dyn AnyParcelable>, + name: String, + }, + Parcel(Parcel), +} + +impl Default for ParcelableHolderData { + fn default() -> Self { + ParcelableHolderData::Empty + } +} + +/// A container that can hold any arbitrary `Parcelable`. +/// +/// This type is currently used for AIDL parcelable fields. +/// +/// `ParcelableHolder` is currently not thread-safe (neither +/// `Send` nor `Sync`), mainly because it internally contains +/// a `Parcel` which in turn is not thread-safe. +#[derive(Debug, Default, Clone)] +pub struct ParcelableHolder { + // This is a `RefCell` because of `get_parcelable` + // which takes `&self` for consistency with C++. + // We could make `get_parcelable` take a `&mut self` + // and get rid of the `RefCell` here for a performance + // improvement, but then callers would require a mutable + // `ParcelableHolder` even for that getter method. + data: RefCell<ParcelableHolderData>, + stability: Stability, +} + +impl ParcelableHolder { + /// Construct a new `ParcelableHolder` with the given stability. + pub fn new(stability: Stability) -> Self { + Self { + data: RefCell::new(ParcelableHolderData::Empty), + stability, + } + } + + /// Reset the contents of this `ParcelableHolder`. + /// + /// Note that this method does not reset the stability, + /// only the contents. + pub fn reset(&mut self) { + *self.data.get_mut() = ParcelableHolderData::Empty; + // We could also clear stability here, but C++ doesn't + } + + /// Set the parcelable contained in this `ParcelableHolder`. + pub fn set_parcelable<T>(&mut self, p: Rc<T>) -> Result<()> + where + T: Any + Parcelable + ParcelableMetadata + std::fmt::Debug, + { + if self.stability > p.get_stability() { + return Err(StatusCode::BAD_VALUE); + } + + *self.data.get_mut() = ParcelableHolderData::Parcelable { + parcelable: p, + name: T::get_descriptor().into(), + }; + + Ok(()) + } + + /// Retrieve the parcelable stored in this `ParcelableHolder`. + /// + /// This method attempts to retrieve the parcelable inside + /// the current object as a parcelable of type `T`. + /// The object is validated against `T` by checking that + /// its parcelable descriptor matches the one returned + /// by `T::get_descriptor()`. + /// + /// Returns one of the following: + /// * `Err(_)` in case of error + /// * `Ok(None)` if the holder is empty or the descriptor does not match + /// * `Ok(Some(_))` if the object holds a parcelable of type `T` + /// with the correct descriptor + pub fn get_parcelable<T>(&self) -> Result<Option<Rc<T>>> + where + T: Any + Parcelable + ParcelableMetadata + Default + std::fmt::Debug, + { + let parcelable_desc = T::get_descriptor(); + let mut data = self.data.borrow_mut(); + match *data { + ParcelableHolderData::Empty => Ok(None), + ParcelableHolderData::Parcelable { + ref parcelable, + ref name, + } => { + if name != parcelable_desc { + return Err(StatusCode::BAD_VALUE); + } + + match Rc::clone(parcelable).downcast_rc::<T>() { + Err(_) => Err(StatusCode::BAD_VALUE), + Ok(x) => Ok(Some(x)), + } + } + ParcelableHolderData::Parcel(ref parcel) => { + unsafe { + // Safety: 0 should always be a valid position. + parcel.set_data_position(0)?; + } + + let name: String = parcel.read()?; + if name != parcelable_desc { + return Ok(None); + } + + let mut parcelable = T::default(); + parcelable.read_from_parcel(parcel)?; + + let parcelable = Rc::new(parcelable); + let result = Rc::clone(&parcelable); + *data = ParcelableHolderData::Parcelable { parcelable, name }; + + Ok(Some(result)) + } + } + } + + /// Return the stability value of this object. + pub fn get_stability(&self) -> Stability { + self.stability + } +} + +impl_serialize_for_parcelable!(ParcelableHolder); +impl_deserialize_for_parcelable!(ParcelableHolder); + +impl Parcelable for ParcelableHolder { + fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()> { + parcel.write(&self.stability)?; + + match *self.data.borrow() { + ParcelableHolderData::Empty => parcel.write(&0i32), + ParcelableHolderData::Parcelable { + ref parcelable, + ref name, + } => { + let length_start = parcel.get_data_position(); + parcel.write(&0i32)?; + + let data_start = parcel.get_data_position(); + parcel.write(name)?; + parcelable.write_to_parcel(parcel)?; + + let end = parcel.get_data_position(); + unsafe { + // Safety: we got the position from `get_data_position`. + parcel.set_data_position(length_start)?; + } + + assert!(end >= data_start); + parcel.write(&(end - data_start))?; + unsafe { + // Safety: we got the position from `get_data_position`. + parcel.set_data_position(end)?; + } + + Ok(()) + } + ParcelableHolderData::Parcel(ref p) => { + parcel.write(&p.get_data_size())?; + parcel.append_all_from(p) + } + } + } + + fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()> { + self.stability = parcel.read()?; + + let data_size: i32 = parcel.read()?; + if data_size < 0 { + // C++ returns BAD_VALUE here, + // while Java returns ILLEGAL_ARGUMENT + return Err(StatusCode::BAD_VALUE); + } + if data_size == 0 { + *self.data.get_mut() = ParcelableHolderData::Empty; + return Ok(()); + } + + // TODO: C++ ParcelableHolder accepts sizes up to SIZE_MAX here, but we + // only go up to i32::MAX because that's what our API uses everywhere + let data_start = parcel.get_data_position(); + let data_end = data_start + .checked_add(data_size) + .ok_or(StatusCode::BAD_VALUE)?; + + let mut new_parcel = Parcel::new(); + new_parcel.append_from(parcel, data_start, data_size)?; + *self.data.get_mut() = ParcelableHolderData::Parcel(new_parcel); + + unsafe { + // Safety: `append_from` checks if `data_size` overflows + // `parcel` and returns `BAD_VALUE` if that happens. We also + // explicitly check for negative and zero `data_size` above, + // so `data_end` is guaranteed to be greater than `data_start`. + parcel.set_data_position(data_end)?; + } + + Ok(()) + } +} |