blob: daa11239ed90a8a92ed4a507e0d09135daa91fb3 [file]
/*
* Copyright (C) 2025 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::AsNative,
error::{status_result, StatusCode},
impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
parcel::{BorrowedParcel, UnstructuredParcelable},
};
use binder_ndk_sys::{
APersistableBundle, APersistableBundle_delete, APersistableBundle_dup,
APersistableBundle_erase, APersistableBundle_getBoolean, APersistableBundle_getBooleanKeys,
APersistableBundle_getBooleanVector, APersistableBundle_getBooleanVectorKeys,
APersistableBundle_getDouble, APersistableBundle_getDoubleKeys,
APersistableBundle_getDoubleVector, APersistableBundle_getDoubleVectorKeys,
APersistableBundle_getInt, APersistableBundle_getIntKeys, APersistableBundle_getIntVector,
APersistableBundle_getIntVectorKeys, APersistableBundle_getLong,
APersistableBundle_getLongKeys, APersistableBundle_getLongVector,
APersistableBundle_getLongVectorKeys, APersistableBundle_getPersistableBundle,
APersistableBundle_getPersistableBundleKeys, APersistableBundle_getString,
APersistableBundle_getStringKeys, APersistableBundle_getStringVector,
APersistableBundle_getStringVectorKeys, APersistableBundle_isEqual, APersistableBundle_new,
APersistableBundle_putBoolean, APersistableBundle_putBooleanVector,
APersistableBundle_putDouble, APersistableBundle_putDoubleVector, APersistableBundle_putInt,
APersistableBundle_putIntVector, APersistableBundle_putLong, APersistableBundle_putLongVector,
APersistableBundle_putPersistableBundle, APersistableBundle_putString,
APersistableBundle_putStringVector, APersistableBundle_readFromParcel, APersistableBundle_size,
APersistableBundle_writeToParcel, APERSISTABLEBUNDLE_ALLOCATOR_FAILED,
APERSISTABLEBUNDLE_KEY_NOT_FOUND,
};
use alloc::boxed::Box;
use alloc::ffi::{CString, NulError};
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::ffi::{c_char, c_void, CStr};
use core::mem::size_of;
use core::ptr::{null_mut, slice_from_raw_parts_mut, NonNull};
use zerocopy::FromZeros;
/// A mapping from string keys to values of various types.
#[derive(Debug)]
pub struct PersistableBundle(NonNull<APersistableBundle>);
impl PersistableBundle {
/// Creates a new `PersistableBundle`.
pub fn new() -> Self {
// SAFETY: APersistableBundle_new doesn't actually have any safety requirements.
let bundle = unsafe { APersistableBundle_new() };
Self(NonNull::new(bundle).expect("Allocated APersistableBundle was null"))
}
/// Returns the number of mappings in the bundle.
pub fn size(&self) -> usize {
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`.
unsafe { APersistableBundle_size(self.0.as_ptr()) }
.try_into()
.expect("APersistableBundle_size returned a negative size")
}
/// Removes any entry with the given key.
///
/// Returns an error if the given key contains a NUL character, otherwise returns whether there
/// was any entry to remove.
pub fn remove(&mut self, key: &str) -> Result<bool, NulError> {
let key = CString::new(key)?;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call.
Ok(unsafe { APersistableBundle_erase(self.0.as_ptr(), key.as_ptr()) != 0 })
}
/// Inserts a key-value pair into the bundle.
///
/// If the key is already present then its value will be overwritten by the given value.
///
/// Returns an error if the key contains a NUL character.
pub fn insert_bool(&mut self, key: &str, value: bool) -> Result<(), NulError> {
let key = CString::new(key)?;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call.
unsafe {
APersistableBundle_putBoolean(self.0.as_ptr(), key.as_ptr(), value);
}
Ok(())
}
/// Inserts a key-value pair into the bundle.
///
/// If the key is already present then its value will be overwritten by the given value.
///
/// Returns an error if the key contains a NUL character.
pub fn insert_int(&mut self, key: &str, value: i32) -> Result<(), NulError> {
let key = CString::new(key)?;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call.
unsafe {
APersistableBundle_putInt(self.0.as_ptr(), key.as_ptr(), value);
}
Ok(())
}
/// Inserts a key-value pair into the bundle.
///
/// If the key is already present then its value will be overwritten by the given value.
///
/// Returns an error if the key contains a NUL character.
pub fn insert_long(&mut self, key: &str, value: i64) -> Result<(), NulError> {
let key = CString::new(key)?;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call.
unsafe {
APersistableBundle_putLong(self.0.as_ptr(), key.as_ptr(), value);
}
Ok(())
}
/// Inserts a key-value pair into the bundle.
///
/// If the key is already present then its value will be overwritten by the given value.
///
/// Returns an error if the key contains a NUL character.
pub fn insert_double(&mut self, key: &str, value: f64) -> Result<(), NulError> {
let key = CString::new(key)?;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call.
unsafe {
APersistableBundle_putDouble(self.0.as_ptr(), key.as_ptr(), value);
}
Ok(())
}
/// Inserts a key-value pair into the bundle.
///
/// If the key is already present then its value will be overwritten by the given value.
///
/// Returns an error if the key or value contains a NUL character.
pub fn insert_string(&mut self, key: &str, value: &str) -> Result<(), NulError> {
let key = CString::new(key)?;
let value = CString::new(value)?;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `CStr::as_ptr` is guaranteed
// to be valid for the duration of this call.
unsafe {
APersistableBundle_putString(self.0.as_ptr(), key.as_ptr(), value.as_ptr());
}
Ok(())
}
/// Inserts a key-value pair into the bundle.
///
/// If the key is already present then its value will be overwritten by the given value.
///
/// Returns an error if the key contains a NUL character.
pub fn insert_bool_vec(&mut self, key: &str, value: &[bool]) -> Result<(), NulError> {
let key = CString::new(key)?;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call, and likewise the pointer returned by
// `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
// duration of the call.
unsafe {
APersistableBundle_putBooleanVector(
self.0.as_ptr(),
key.as_ptr(),
value.as_ptr(),
value.len().try_into().unwrap(),
);
}
Ok(())
}
/// Inserts a key-value pair into the bundle.
///
/// If the key is already present then its value will be overwritten by the given value.
///
/// Returns an error if the key contains a NUL character.
pub fn insert_int_vec(&mut self, key: &str, value: &[i32]) -> Result<(), NulError> {
let key = CString::new(key)?;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call, and likewise the pointer returned by
// `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
// duration of the call.
unsafe {
APersistableBundle_putIntVector(
self.0.as_ptr(),
key.as_ptr(),
value.as_ptr(),
value.len().try_into().unwrap(),
);
}
Ok(())
}
/// Inserts a key-value pair into the bundle.
///
/// If the key is already present then its value will be overwritten by the given value.
///
/// Returns an error if the key contains a NUL character.
pub fn insert_long_vec(&mut self, key: &str, value: &[i64]) -> Result<(), NulError> {
let key = CString::new(key)?;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call, and likewise the pointer returned by
// `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
// duration of the call.
unsafe {
APersistableBundle_putLongVector(
self.0.as_ptr(),
key.as_ptr(),
value.as_ptr(),
value.len().try_into().unwrap(),
);
}
Ok(())
}
/// Inserts a key-value pair into the bundle.
///
/// If the key is already present then its value will be overwritten by the given value.
///
/// Returns an error if the key contains a NUL character.
pub fn insert_double_vec(&mut self, key: &str, value: &[f64]) -> Result<(), NulError> {
let key = CString::new(key)?;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call, and likewise the pointer returned by
// `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
// duration of the call.
unsafe {
APersistableBundle_putDoubleVector(
self.0.as_ptr(),
key.as_ptr(),
value.as_ptr(),
value.len().try_into().unwrap(),
);
}
Ok(())
}
/// Inserts a key-value pair into the bundle.
///
/// If the key is already present then its value will be overwritten by the given value.
///
/// Returns an error if the key contains a NUL character.
pub fn insert_string_vec<'a, T: ToString + 'a>(
&mut self,
key: &str,
value: impl IntoIterator<Item = &'a T>,
) -> Result<(), NulError> {
let key = CString::new(key)?;
// We need to collect the new `CString`s into something first so that they live long enough
// for their pointers to be valid for the `APersistableBundle_putStringVector` call below.
let c_strings = value
.into_iter()
.map(|s| CString::new(s.to_string()))
.collect::<Result<Vec<_>, NulError>>()?;
let char_pointers = c_strings.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call, and likewise the pointer returned by
// `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
// duration of the call.
unsafe {
APersistableBundle_putStringVector(
self.0.as_ptr(),
key.as_ptr(),
char_pointers.as_ptr(),
char_pointers.len().try_into().unwrap(),
);
}
Ok(())
}
/// Inserts a key-value pair into the bundle.
///
/// If the key is already present then its value will be overwritten by the given value.
///
/// Returns an error if the key contains a NUL character.
pub fn insert_persistable_bundle(
&mut self,
key: &str,
value: &PersistableBundle,
) -> Result<(), NulError> {
let key = CString::new(key)?;
// SAFETY: The wrapped `APersistableBundle` pointers are guaranteed to be valid for the
// lifetime of the `PersistableBundle`s. The pointer returned by `CStr::as_ptr` is
// guaranteed to be valid for the duration of this call, and
// `APersistableBundle_putPersistableBundle` does a deep copy so that is all that is
// required.
unsafe {
APersistableBundle_putPersistableBundle(
self.0.as_ptr(),
key.as_ptr(),
value.0.as_ptr(),
);
}
Ok(())
}
/// Gets the boolean value associated with the given key.
///
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
/// in the bundle.
pub fn get_bool(&self, key: &str) -> Result<Option<bool>, NulError> {
let key = CString::new(key)?;
let mut value = false;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call. The value pointer must be valid because it
// comes from a reference.
if unsafe { APersistableBundle_getBoolean(self.0.as_ptr(), key.as_ptr(), &mut value) } {
Ok(Some(value))
} else {
Ok(None)
}
}
/// Gets the i32 value associated with the given key.
///
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
/// in the bundle.
pub fn get_int(&self, key: &str) -> Result<Option<i32>, NulError> {
let key = CString::new(key)?;
let mut value = 0;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call. The value pointer must be valid because it
// comes from a reference.
if unsafe { APersistableBundle_getInt(self.0.as_ptr(), key.as_ptr(), &mut value) } {
Ok(Some(value))
} else {
Ok(None)
}
}
/// Gets the i64 value associated with the given key.
///
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
/// in the bundle.
pub fn get_long(&self, key: &str) -> Result<Option<i64>, NulError> {
let key = CString::new(key)?;
let mut value = 0;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call. The value pointer must be valid because it
// comes from a reference.
if unsafe { APersistableBundle_getLong(self.0.as_ptr(), key.as_ptr(), &mut value) } {
Ok(Some(value))
} else {
Ok(None)
}
}
/// Gets the f64 value associated with the given key.
///
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
/// in the bundle.
pub fn get_double(&self, key: &str) -> Result<Option<f64>, NulError> {
let key = CString::new(key)?;
let mut value = 0.0;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the duration of this call. The value pointer must be valid because it
// comes from a reference.
if unsafe { APersistableBundle_getDouble(self.0.as_ptr(), key.as_ptr(), &mut value) } {
Ok(Some(value))
} else {
Ok(None)
}
}
/// Gets the string value associated with the given key.
///
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
/// in the bundle.
pub fn get_string(&self, key: &str) -> Result<Option<String>, NulError> {
let key = CString::new(key)?;
let mut value = null_mut();
let mut allocated_size: usize = 0;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the lifetime of `key`. The value pointer must be valid because it comes
// from a reference.
let value_size_bytes = unsafe {
APersistableBundle_getString(
self.0.as_ptr(),
key.as_ptr(),
&mut value,
Some(string_allocator),
(&raw mut allocated_size).cast(),
)
};
match value_size_bytes {
APERSISTABLEBUNDLE_KEY_NOT_FOUND => Ok(None),
APERSISTABLEBUNDLE_ALLOCATOR_FAILED => {
panic!("APersistableBundle_getString failed to allocate string");
}
_ => {
let raw_slice = slice_from_raw_parts_mut(value.cast(), allocated_size);
// SAFETY: The pointer was returned from string_allocator, which used
// `Box::into_raw`, and we've got the appropriate size back from allocated_size.
let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) };
assert_eq!(
allocated_size,
usize::try_from(value_size_bytes)
.expect("APersistableBundle_getString returned negative value size")
+ 1
);
let c_string = CString::from_vec_with_nul(boxed_slice.into())
.expect("APersistableBundle_getString returned string missing NUL byte");
let string = c_string
.into_string()
.expect("APersistableBundle_getString returned invalid UTF-8");
Ok(Some(string))
}
}
}
/// Gets the vector of `T` associated with the given key.
///
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
/// in the bundle.
///
/// `get_func` should be one of the `APersistableBundle_get*Vector` functions from
/// `binder_ndk_sys`.
///
/// # Safety
///
/// `get_func` must only require that the pointers it takes are valid for the duration of the
/// call. It must allow a null pointer for the buffer, and must return the size in bytes of
/// buffer it requires. If it is given a non-null buffer pointer it must write that number of
/// bytes to the buffer, which must be a whole number of valid `T` values.
unsafe fn get_vec<T: Clone>(
&self,
key: &str,
default: T,
get_func: unsafe extern "C" fn(
*const APersistableBundle,
*const c_char,
*mut T,
i32,
) -> i32,
) -> Result<Option<Vec<T>>, NulError> {
let key = CString::new(key)?;
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the lifetime of `key`. A null pointer is allowed for the buffer.
match unsafe { get_func(self.0.as_ptr(), key.as_ptr(), null_mut(), 0) } {
APERSISTABLEBUNDLE_KEY_NOT_FOUND => Ok(None),
APERSISTABLEBUNDLE_ALLOCATOR_FAILED => {
panic!("APersistableBundle_getStringVector failed to allocate string");
}
required_buffer_size => {
let mut value = vec![
default;
usize::try_from(required_buffer_size).expect(
"APersistableBundle_get*Vector returned invalid size"
) / size_of::<T>()
];
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for
// the lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()`
// is guaranteed to be valid for the lifetime of `key`. The value buffer pointer is
// valid as it comes from the Vec we just allocated.
match unsafe {
get_func(
self.0.as_ptr(),
key.as_ptr(),
value.as_mut_ptr(),
(value.len() * size_of::<T>()).try_into().unwrap(),
)
} {
APERSISTABLEBUNDLE_KEY_NOT_FOUND => {
panic!("APersistableBundle_get*Vector failed to find key after first finding it");
}
APERSISTABLEBUNDLE_ALLOCATOR_FAILED => {
panic!("APersistableBundle_getStringVector failed to allocate string");
}
_ => Ok(Some(value)),
}
}
}
}
/// Gets the boolean vector value associated with the given key.
///
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
/// in the bundle.
pub fn get_bool_vec(&self, key: &str) -> Result<Option<Vec<bool>>, NulError> {
// SAFETY: APersistableBundle_getBooleanVector fulfils all the safety requirements of
// `get_vec`.
unsafe { self.get_vec(key, Default::default(), APersistableBundle_getBooleanVector) }
}
/// Gets the i32 vector value associated with the given key.
///
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
/// in the bundle.
pub fn get_int_vec(&self, key: &str) -> Result<Option<Vec<i32>>, NulError> {
// SAFETY: APersistableBundle_getIntVector fulfils all the safety requirements of
// `get_vec`.
unsafe { self.get_vec(key, Default::default(), APersistableBundle_getIntVector) }
}
/// Gets the i64 vector value associated with the given key.
///
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
/// in the bundle.
pub fn get_long_vec(&self, key: &str) -> Result<Option<Vec<i64>>, NulError> {
// SAFETY: APersistableBundle_getLongVector fulfils all the safety requirements of
// `get_vec`.
unsafe { self.get_vec(key, Default::default(), APersistableBundle_getLongVector) }
}
/// Gets the f64 vector value associated with the given key.
///
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
/// in the bundle.
pub fn get_double_vec(&self, key: &str) -> Result<Option<Vec<f64>>, NulError> {
// SAFETY: APersistableBundle_getDoubleVector fulfils all the safety requirements of
// `get_vec`.
unsafe { self.get_vec(key, Default::default(), APersistableBundle_getDoubleVector) }
}
/// Gets the string vector value associated with the given key.
///
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
/// in the bundle.
pub fn get_string_vec(&self, key: &str) -> Result<Option<Vec<String>>, NulError> {
if let Some(value) =
// SAFETY: `get_string_vector_with_allocator` fulfils all the safety requirements of
// `get_vec`.
unsafe { self.get_vec(key, null_mut(), get_string_vector_with_allocator) }?
{
Ok(Some(
value
.into_iter()
.map(|s| {
// SAFETY: The pointer was returned from `string_allocator`, which used
// `Box::into_raw`, and `APersistableBundle_getStringVector` should have
// written valid bytes to it including a NUL terminator in the last
// position.
let string_length = unsafe { CStr::from_ptr(s) }.count_bytes();
let raw_slice = slice_from_raw_parts_mut(s.cast(), string_length + 1);
// SAFETY: The pointer was returned from `string_allocator`, which used
// `Box::into_raw`, and we've got the appropriate size back by checking the
// length of the string.
let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) };
let c_string = CString::from_vec_with_nul(boxed_slice.into()).expect(
"APersistableBundle_getStringVector returned string missing NUL byte",
);
c_string
.into_string()
.expect("APersistableBundle_getStringVector returned invalid UTF-8")
})
.collect(),
))
} else {
Ok(None)
}
}
/// Gets the `PersistableBundle` value associated with the given key.
///
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
/// in the bundle.
pub fn get_persistable_bundle(&self, key: &str) -> Result<Option<Self>, NulError> {
let key = CString::new(key)?;
let mut value = null_mut();
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
// to be valid for the lifetime of `key`. The value pointer must be valid because it comes
// from a reference.
if unsafe {
APersistableBundle_getPersistableBundle(self.0.as_ptr(), key.as_ptr(), &mut value)
} {
Ok(Some(Self(NonNull::new(value).expect(
"APersistableBundle_getPersistableBundle returned true but didn't set outBundle",
))))
} else {
Ok(None)
}
}
/// Calls the appropriate `APersistableBundle_get*Keys` function for the given `value_type`,
/// with our `string_allocator` and a null context pointer.
///
/// # Safety
///
/// `out_keys` must either be null or point to a buffer of at least `buffer_size_bytes` bytes,
/// properly aligned for `T`, and not otherwise accessed for the duration of the call.
unsafe fn get_keys_raw(
&self,
value_type: ValueType,
out_keys: *mut *mut c_char,
buffer_size_bytes: i32,
) -> i32 {
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. Our caller guarantees an appropriate value for
// `out_keys` and `buffer_size_bytes`.
unsafe {
match value_type {
ValueType::Boolean => APersistableBundle_getBooleanKeys(
self.0.as_ptr(),
out_keys,
buffer_size_bytes,
Some(string_allocator),
null_mut(),
),
ValueType::Integer => APersistableBundle_getIntKeys(
self.0.as_ptr(),
out_keys,
buffer_size_bytes,
Some(string_allocator),
null_mut(),
),
ValueType::Long => APersistableBundle_getLongKeys(
self.0.as_ptr(),
out_keys,
buffer_size_bytes,
Some(string_allocator),
null_mut(),
),
ValueType::Double => APersistableBundle_getDoubleKeys(
self.0.as_ptr(),
out_keys,
buffer_size_bytes,
Some(string_allocator),
null_mut(),
),
ValueType::String => APersistableBundle_getStringKeys(
self.0.as_ptr(),
out_keys,
buffer_size_bytes,
Some(string_allocator),
null_mut(),
),
ValueType::BooleanVector => APersistableBundle_getBooleanVectorKeys(
self.0.as_ptr(),
out_keys,
buffer_size_bytes,
Some(string_allocator),
null_mut(),
),
ValueType::IntegerVector => APersistableBundle_getIntVectorKeys(
self.0.as_ptr(),
out_keys,
buffer_size_bytes,
Some(string_allocator),
null_mut(),
),
ValueType::LongVector => APersistableBundle_getLongVectorKeys(
self.0.as_ptr(),
out_keys,
buffer_size_bytes,
Some(string_allocator),
null_mut(),
),
ValueType::DoubleVector => APersistableBundle_getDoubleVectorKeys(
self.0.as_ptr(),
out_keys,
buffer_size_bytes,
Some(string_allocator),
null_mut(),
),
ValueType::StringVector => APersistableBundle_getStringVectorKeys(
self.0.as_ptr(),
out_keys,
buffer_size_bytes,
Some(string_allocator),
null_mut(),
),
ValueType::PersistableBundle => APersistableBundle_getPersistableBundleKeys(
self.0.as_ptr(),
out_keys,
buffer_size_bytes,
Some(string_allocator),
null_mut(),
),
}
}
}
/// Gets all the keys associated with values of the given type.
pub fn keys_for_type(&self, value_type: ValueType) -> Vec<String> {
// SAFETY: A null pointer is allowed for the buffer.
match unsafe { self.get_keys_raw(value_type, null_mut(), 0) } {
APERSISTABLEBUNDLE_ALLOCATOR_FAILED => {
panic!("APersistableBundle_get*Keys failed to allocate string");
}
required_buffer_size => {
let required_buffer_size_usize = usize::try_from(required_buffer_size)
.expect("APersistableBundle_get*Keys returned invalid size");
assert_eq!(required_buffer_size_usize % size_of::<*mut c_char>(), 0);
let mut keys =
vec![null_mut(); required_buffer_size_usize / size_of::<*mut c_char>()];
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for
// the lifetime of the `PersistableBundle`. The keys buffer pointer is valid as it
// comes from the Vec we just allocated.
if unsafe { self.get_keys_raw(value_type, keys.as_mut_ptr(), required_buffer_size) }
== APERSISTABLEBUNDLE_ALLOCATOR_FAILED
{
panic!("APersistableBundle_get*Keys failed to allocate string");
}
keys.into_iter()
.map(|key| {
// SAFETY: The pointer was returned from `string_allocator`, which used
// `Box::into_raw`, and `APersistableBundle_getStringVector` should have
// written valid bytes to it including a NUL terminator in the last
// position.
let string_length = unsafe { CStr::from_ptr(key) }.count_bytes();
let raw_slice = slice_from_raw_parts_mut(key.cast(), string_length + 1);
// SAFETY: The pointer was returned from `string_allocator`, which used
// `Box::into_raw`, and we've got the appropriate size back by checking the
// length of the string.
let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) };
let c_string = CString::from_vec_with_nul(boxed_slice.into())
.expect("APersistableBundle_get*Keys returned string missing NUL byte");
c_string
.into_string()
.expect("APersistableBundle_get*Keys returned invalid UTF-8")
})
.collect()
}
}
}
/// Returns an iterator over all keys in the bundle, along with the type of their associated
/// value.
pub fn keys(&self) -> impl Iterator<Item = (String, ValueType)> + use<'_> {
[
ValueType::Boolean,
ValueType::Integer,
ValueType::Long,
ValueType::Double,
ValueType::String,
ValueType::BooleanVector,
ValueType::IntegerVector,
ValueType::LongVector,
ValueType::DoubleVector,
ValueType::StringVector,
ValueType::PersistableBundle,
]
.iter()
.flat_map(|value_type| {
self.keys_for_type(*value_type).into_iter().map(|key| (key, *value_type))
})
}
}
/// Wrapper around `APersistableBundle_getStringVector` to pass `string_allocator` and a null
/// context pointer.
///
/// # Safety
///
/// * `bundle` must point to a valid `APersistableBundle` which is not modified for the duration of
/// the call.
/// * `key` must point to a valid NUL-terminated C string.
/// * `buffer` must either be null or point to a buffer of at least `buffer_size_bytes` bytes,
/// properly aligned for `T`, and not otherwise accessed for the duration of the call.
unsafe extern "C" fn get_string_vector_with_allocator(
bundle: *const APersistableBundle,
key: *const c_char,
buffer: *mut *mut c_char,
buffer_size_bytes: i32,
) -> i32 {
// SAFETY: The safety requirements are all guaranteed by our caller according to the safety
// documentation above.
unsafe {
APersistableBundle_getStringVector(
bundle,
key,
buffer,
buffer_size_bytes,
Some(string_allocator),
null_mut(),
)
}
}
// SAFETY: The underlying *APersistableBundle can be moved between threads.
unsafe impl Send for PersistableBundle {}
// SAFETY: The underlying *APersistableBundle can be read from multiple threads, and we require
// `&mut PersistableBundle` for any operations which mutate it.
unsafe impl Sync for PersistableBundle {}
impl Default for PersistableBundle {
fn default() -> Self {
Self::new()
}
}
impl Drop for PersistableBundle {
fn drop(&mut self) {
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of this `PersistableBundle`.
unsafe { APersistableBundle_delete(self.0.as_ptr()) };
}
}
impl Clone for PersistableBundle {
fn clone(&self) -> Self {
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`.
let duplicate = unsafe { APersistableBundle_dup(self.0.as_ptr()) };
Self(NonNull::new(duplicate).expect("Duplicated APersistableBundle was null"))
}
}
impl PartialEq for PersistableBundle {
fn eq(&self, other: &Self) -> bool {
// SAFETY: The wrapped `APersistableBundle` pointers are guaranteed to be valid for the
// lifetime of the `PersistableBundle`s.
unsafe { APersistableBundle_isEqual(self.0.as_ptr(), other.0.as_ptr()) }
}
}
impl UnstructuredParcelable for PersistableBundle {
fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
let status =
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. `parcel.as_native_mut()` always returns a valid
// parcel pointer.
unsafe { APersistableBundle_writeToParcel(self.0.as_ptr(), parcel.as_native_mut()) };
status_result(status)
}
fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
let mut bundle = null_mut();
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
// lifetime of the `PersistableBundle`. `parcel.as_native()` always returns a valid parcel
// pointer.
let status = unsafe { APersistableBundle_readFromParcel(parcel.as_native(), &mut bundle) };
status_result(status)?;
Ok(Self(NonNull::new(bundle).expect(
"APersistableBundle_readFromParcel returned success but didn't allocate bundle",
)))
}
}
/// Allocates a boxed slice of the given size in bytes, returns a pointer to it and writes its size
/// to `*context`.
///
/// # Safety
///
/// `context` must either be null or point to a `usize` to which we can write.
unsafe extern "C" fn string_allocator(size: i32, context: *mut c_void) -> *mut c_char {
let Ok(size) = size.try_into() else {
return null_mut();
};
let Ok(boxed_slice) = <[c_char]>::new_box_zeroed_with_elems(size) else {
return null_mut();
};
if !context.is_null() {
// SAFETY: The caller promised that `context` is either null or points to a `usize` to which
// we can write, and we just checked that it's not null.
unsafe {
*context.cast::<usize>() = size;
}
}
Box::into_raw(boxed_slice).cast()
}
impl_deserialize_for_unstructured_parcelable!(PersistableBundle);
impl_serialize_for_unstructured_parcelable!(PersistableBundle);
/// The types which may be stored as values in a [`PersistableBundle`].
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ValueType {
/// A `bool`.
Boolean,
/// An `i32`.
Integer,
/// An `i64`.
Long,
/// An `f64`.
Double,
/// A string.
String,
/// A vector of `bool`s.
BooleanVector,
/// A vector of `i32`s.
IntegerVector,
/// A vector of `i64`s.
LongVector,
/// A vector of `f64`s.
DoubleVector,
/// A vector of strings.
StringVector,
/// A nested `PersistableBundle`.
PersistableBundle,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn create_delete() {
let bundle = PersistableBundle::new();
drop(bundle);
}
#[test]
fn duplicate_equal() {
let bundle = PersistableBundle::new();
let duplicate = bundle.clone();
assert_eq!(bundle, duplicate);
}
#[test]
fn get_empty() {
let bundle = PersistableBundle::new();
assert_eq!(bundle.get_bool("foo"), Ok(None));
assert_eq!(bundle.get_int("foo"), Ok(None));
assert_eq!(bundle.get_long("foo"), Ok(None));
assert_eq!(bundle.get_double("foo"), Ok(None));
assert_eq!(bundle.get_bool_vec("foo"), Ok(None));
assert_eq!(bundle.get_int_vec("foo"), Ok(None));
assert_eq!(bundle.get_long_vec("foo"), Ok(None));
assert_eq!(bundle.get_double_vec("foo"), Ok(None));
assert_eq!(bundle.get_string("foo"), Ok(None));
}
#[test]
fn remove_empty() {
let mut bundle = PersistableBundle::new();
assert_eq!(bundle.remove("foo"), Ok(false));
}
#[test]
fn insert_get_primitives() {
let mut bundle = PersistableBundle::new();
assert_eq!(bundle.insert_bool("bool", true), Ok(()));
assert_eq!(bundle.insert_int("int", 42), Ok(()));
assert_eq!(bundle.insert_long("long", 66), Ok(()));
assert_eq!(bundle.insert_double("double", 123.4), Ok(()));
assert_eq!(bundle.get_bool("bool"), Ok(Some(true)));
assert_eq!(bundle.get_int("int"), Ok(Some(42)));
assert_eq!(bundle.get_long("long"), Ok(Some(66)));
assert_eq!(bundle.get_double("double"), Ok(Some(123.4)));
assert_eq!(bundle.size(), 4);
// Getting the wrong type should return nothing.
assert_eq!(bundle.get_int("bool"), Ok(None));
assert_eq!(bundle.get_long("bool"), Ok(None));
assert_eq!(bundle.get_double("bool"), Ok(None));
assert_eq!(bundle.get_bool("int"), Ok(None));
assert_eq!(bundle.get_long("int"), Ok(None));
assert_eq!(bundle.get_double("int"), Ok(None));
assert_eq!(bundle.get_bool("long"), Ok(None));
assert_eq!(bundle.get_int("long"), Ok(None));
assert_eq!(bundle.get_double("long"), Ok(None));
assert_eq!(bundle.get_bool("double"), Ok(None));
assert_eq!(bundle.get_int("double"), Ok(None));
assert_eq!(bundle.get_long("double"), Ok(None));
// If they are removed they should no longer be present.
assert_eq!(bundle.remove("bool"), Ok(true));
assert_eq!(bundle.remove("int"), Ok(true));
assert_eq!(bundle.remove("long"), Ok(true));
assert_eq!(bundle.remove("double"), Ok(true));
assert_eq!(bundle.get_bool("bool"), Ok(None));
assert_eq!(bundle.get_int("int"), Ok(None));
assert_eq!(bundle.get_long("long"), Ok(None));
assert_eq!(bundle.get_double("double"), Ok(None));
assert_eq!(bundle.size(), 0);
}
#[test]
fn insert_get_string() {
let mut bundle = PersistableBundle::new();
assert_eq!(bundle.insert_string("string", "foo"), Ok(()));
assert_eq!(bundle.insert_string("empty", ""), Ok(()));
assert_eq!(bundle.size(), 2);
assert_eq!(bundle.get_string("string"), Ok(Some("foo".to_string())));
assert_eq!(bundle.get_string("empty"), Ok(Some("".to_string())));
}
#[test]
fn insert_get_vec() {
let mut bundle = PersistableBundle::new();
assert_eq!(bundle.insert_bool_vec("bool", &[]), Ok(()));
assert_eq!(bundle.insert_int_vec("int", &[42]), Ok(()));
assert_eq!(bundle.insert_long_vec("long", &[66, 67, 68]), Ok(()));
assert_eq!(bundle.insert_double_vec("double", &[123.4]), Ok(()));
assert_eq!(bundle.insert_string_vec("string", &["foo", "bar", "baz"]), Ok(()));
assert_eq!(
bundle.insert_string_vec(
"string",
&[&"foo".to_string(), &"bar".to_string(), &"baz".to_string()]
),
Ok(())
);
assert_eq!(
bundle.insert_string_vec(
"string",
&["foo".to_string(), "bar".to_string(), "baz".to_string()]
),
Ok(())
);
assert_eq!(bundle.size(), 5);
assert_eq!(bundle.get_bool_vec("bool"), Ok(Some(vec![])));
assert_eq!(bundle.get_int_vec("int"), Ok(Some(vec![42])));
assert_eq!(bundle.get_long_vec("long"), Ok(Some(vec![66, 67, 68])));
assert_eq!(bundle.get_double_vec("double"), Ok(Some(vec![123.4])));
assert_eq!(
bundle.get_string_vec("string"),
Ok(Some(vec!["foo".to_string(), "bar".to_string(), "baz".to_string()]))
);
}
#[test]
fn insert_get_bundle() {
let mut bundle = PersistableBundle::new();
let mut sub_bundle = PersistableBundle::new();
assert_eq!(sub_bundle.insert_int("int", 42), Ok(()));
assert_eq!(sub_bundle.size(), 1);
assert_eq!(bundle.insert_persistable_bundle("bundle", &sub_bundle), Ok(()));
assert_eq!(bundle.get_persistable_bundle("bundle"), Ok(Some(sub_bundle)));
}
#[test]
fn get_keys() {
let mut bundle = PersistableBundle::new();
assert_eq!(bundle.keys_for_type(ValueType::Boolean), Vec::<String>::new());
assert_eq!(bundle.keys_for_type(ValueType::Integer), Vec::<String>::new());
assert_eq!(bundle.keys_for_type(ValueType::StringVector), Vec::<String>::new());
assert_eq!(bundle.insert_bool("bool1", false), Ok(()));
assert_eq!(bundle.insert_bool("bool2", true), Ok(()));
assert_eq!(bundle.insert_int("int", 42), Ok(()));
assert_eq!(
bundle.keys_for_type(ValueType::Boolean),
vec!["bool1".to_string(), "bool2".to_string()]
);
assert_eq!(bundle.keys_for_type(ValueType::Integer), vec!["int".to_string()]);
assert_eq!(bundle.keys_for_type(ValueType::StringVector), Vec::<String>::new());
assert_eq!(
bundle.keys().collect::<Vec<_>>(),
vec![
("bool1".to_string(), ValueType::Boolean),
("bool2".to_string(), ValueType::Boolean),
("int".to_string(), ValueType::Integer),
]
);
}
}