Merge "Add method to get string value." into main am: 37a49f2ac6 am: e4f9f5c93c
Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/3452201
Change-Id: I939a6cf7ff3dfc183c9f6ecb4a78d8813a2a6e3d
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 8404a48..adef9ea 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -16,6 +16,7 @@
"libdowncast_rs",
"liblibc",
"liblog_rust",
+ "libzerocopy",
],
host_supported: true,
vendor_available: true,
@@ -205,6 +206,7 @@
"libdowncast_rs",
"liblibc",
"liblog_rust",
+ "libzerocopy",
],
}
diff --git a/libs/binder/rust/src/persistable_bundle.rs b/libs/binder/rust/src/persistable_bundle.rs
index d71ed73..46d0fe7 100644
--- a/libs/binder/rust/src/persistable_bundle.rs
+++ b/libs/binder/rust/src/persistable_bundle.rs
@@ -25,16 +25,19 @@
APersistableBundle_erase, APersistableBundle_getBoolean, APersistableBundle_getBooleanVector,
APersistableBundle_getDouble, APersistableBundle_getDoubleVector, APersistableBundle_getInt,
APersistableBundle_getIntVector, APersistableBundle_getLong, APersistableBundle_getLongVector,
- APersistableBundle_getPersistableBundle, APersistableBundle_isEqual, APersistableBundle_new,
- APersistableBundle_putBoolean, APersistableBundle_putBooleanVector,
- APersistableBundle_putDouble, APersistableBundle_putDoubleVector, APersistableBundle_putInt,
- APersistableBundle_putIntVector, APersistableBundle_putLong, APersistableBundle_putLongVector,
+ APersistableBundle_getPersistableBundle, APersistableBundle_getString,
+ 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_KEY_NOT_FOUND,
+ APersistableBundle_writeToParcel, APERSISTABLEBUNDLE_ALLOCATOR_FAILED,
+ APERSISTABLEBUNDLE_KEY_NOT_FOUND,
};
-use std::ffi::{c_char, CString, NulError};
-use std::ptr::{null_mut, NonNull};
+use std::ffi::{c_char, c_void, CString, NulError};
+use std::ptr::{null_mut, slice_from_raw_parts_mut, NonNull};
+use zerocopy::FromZeros;
/// A mapping from string keys to values of various types.
#[derive(Debug)]
@@ -374,6 +377,53 @@
}
}
+ /// 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
@@ -558,6 +608,26 @@
}
}
+/// Allocates a boxed slice of the given size in bytes, returns a pointer to it and writes its size
+/// to `*context`.
+///
+/// # Safety
+///
+/// `context` must 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();
+ };
+ // SAFETY: The caller promised that `context` points to a `usize` to which we can write.
+ unsafe {
+ *context.cast::<usize>() = size;
+ }
+ Box::into_raw(boxed_slice).cast()
+}
+
impl_deserialize_for_unstructured_parcelable!(PersistableBundle);
impl_serialize_for_unstructured_parcelable!(PersistableBundle);
@@ -589,6 +659,7 @@
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]
@@ -639,10 +710,15 @@
}
#[test]
- fn insert_string() {
+ fn insert_get_string() {
let mut bundle = PersistableBundle::new();
+
assert_eq!(bundle.insert_string("string", "foo"), Ok(()));
- assert_eq!(bundle.size(), 1);
+ 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]