Skip to content

Commit 37a49f2

Browse files
Treehugger RobotGerrit Code Review
authored andcommitted
Merge "Add method to get string value." into main
2 parents 7317ccb + 0844f47 commit 37a49f2

2 files changed

Lines changed: 87 additions & 9 deletions

File tree

libs/binder/rust/Android.bp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ rust_library {
1616
"libdowncast_rs",
1717
"liblibc",
1818
"liblog_rust",
19+
"libzerocopy",
1920
],
2021
host_supported: true,
2122
vendor_available: true,
@@ -205,6 +206,7 @@ rust_test {
205206
"libdowncast_rs",
206207
"liblibc",
207208
"liblog_rust",
209+
"libzerocopy",
208210
],
209211
}
210212

libs/binder/rust/src/persistable_bundle.rs

Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,19 @@ use binder_ndk_sys::{
2525
APersistableBundle_erase, APersistableBundle_getBoolean, APersistableBundle_getBooleanVector,
2626
APersistableBundle_getDouble, APersistableBundle_getDoubleVector, APersistableBundle_getInt,
2727
APersistableBundle_getIntVector, APersistableBundle_getLong, APersistableBundle_getLongVector,
28-
APersistableBundle_getPersistableBundle, APersistableBundle_isEqual, APersistableBundle_new,
29-
APersistableBundle_putBoolean, APersistableBundle_putBooleanVector,
30-
APersistableBundle_putDouble, APersistableBundle_putDoubleVector, APersistableBundle_putInt,
31-
APersistableBundle_putIntVector, APersistableBundle_putLong, APersistableBundle_putLongVector,
28+
APersistableBundle_getPersistableBundle, APersistableBundle_getString,
29+
APersistableBundle_isEqual, APersistableBundle_new, APersistableBundle_putBoolean,
30+
APersistableBundle_putBooleanVector, APersistableBundle_putDouble,
31+
APersistableBundle_putDoubleVector, APersistableBundle_putInt, APersistableBundle_putIntVector,
32+
APersistableBundle_putLong, APersistableBundle_putLongVector,
3233
APersistableBundle_putPersistableBundle, APersistableBundle_putString,
3334
APersistableBundle_putStringVector, APersistableBundle_readFromParcel, APersistableBundle_size,
34-
APersistableBundle_writeToParcel, APERSISTABLEBUNDLE_KEY_NOT_FOUND,
35+
APersistableBundle_writeToParcel, APERSISTABLEBUNDLE_ALLOCATOR_FAILED,
36+
APERSISTABLEBUNDLE_KEY_NOT_FOUND,
3537
};
36-
use std::ffi::{c_char, CString, NulError};
37-
use std::ptr::{null_mut, NonNull};
38+
use std::ffi::{c_char, c_void, CString, NulError};
39+
use std::ptr::{null_mut, slice_from_raw_parts_mut, NonNull};
40+
use zerocopy::FromZeros;
3841

3942
/// A mapping from string keys to values of various types.
4043
#[derive(Debug)]
@@ -374,6 +377,53 @@ impl PersistableBundle {
374377
}
375378
}
376379

380+
/// Gets the string value associated with the given key.
381+
///
382+
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
383+
/// in the bundle.
384+
pub fn get_string(&self, key: &str) -> Result<Option<String>, NulError> {
385+
let key = CString::new(key)?;
386+
let mut value = null_mut();
387+
let mut allocated_size: usize = 0;
388+
// SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
389+
// lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
390+
// to be valid for the lifetime of `key`. The value pointer must be valid because it comes
391+
// from a reference.
392+
let value_size_bytes = unsafe {
393+
APersistableBundle_getString(
394+
self.0.as_ptr(),
395+
key.as_ptr(),
396+
&mut value,
397+
Some(string_allocator),
398+
(&raw mut allocated_size).cast(),
399+
)
400+
};
401+
match value_size_bytes {
402+
APERSISTABLEBUNDLE_KEY_NOT_FOUND => Ok(None),
403+
APERSISTABLEBUNDLE_ALLOCATOR_FAILED => {
404+
panic!("APersistableBundle_getString failed to allocate string");
405+
}
406+
_ => {
407+
let raw_slice = slice_from_raw_parts_mut(value.cast(), allocated_size);
408+
// SAFETY: The pointer was returned from string_allocator, which used
409+
// `Box::into_raw`, and we've got the appropriate size back from allocated_size.
410+
let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) };
411+
assert_eq!(
412+
allocated_size,
413+
usize::try_from(value_size_bytes)
414+
.expect("APersistableBundle_getString returned negative value size")
415+
+ 1
416+
);
417+
let c_string = CString::from_vec_with_nul(boxed_slice.into())
418+
.expect("APersistableBundle_getString returned string missing NUL byte");
419+
let string = c_string
420+
.into_string()
421+
.expect("APersistableBundle_getString returned invalid UTF-8");
422+
Ok(Some(string))
423+
}
424+
}
425+
}
426+
377427
/// Gets the vector of `T` associated with the given key.
378428
///
379429
/// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
@@ -558,6 +608,26 @@ impl UnstructuredParcelable for PersistableBundle {
558608
}
559609
}
560610

611+
/// Allocates a boxed slice of the given size in bytes, returns a pointer to it and writes its size
612+
/// to `*context`.
613+
///
614+
/// # Safety
615+
///
616+
/// `context` must point to a `usize` to which we can write.
617+
unsafe extern "C" fn string_allocator(size: i32, context: *mut c_void) -> *mut c_char {
618+
let Ok(size) = size.try_into() else {
619+
return null_mut();
620+
};
621+
let Ok(boxed_slice) = <[c_char]>::new_box_zeroed_with_elems(size) else {
622+
return null_mut();
623+
};
624+
// SAFETY: The caller promised that `context` points to a `usize` to which we can write.
625+
unsafe {
626+
*context.cast::<usize>() = size;
627+
}
628+
Box::into_raw(boxed_slice).cast()
629+
}
630+
561631
impl_deserialize_for_unstructured_parcelable!(PersistableBundle);
562632
impl_serialize_for_unstructured_parcelable!(PersistableBundle);
563633

@@ -589,6 +659,7 @@ mod test {
589659
assert_eq!(bundle.get_int_vec("foo"), Ok(None));
590660
assert_eq!(bundle.get_long_vec("foo"), Ok(None));
591661
assert_eq!(bundle.get_double_vec("foo"), Ok(None));
662+
assert_eq!(bundle.get_string("foo"), Ok(None));
592663
}
593664

594665
#[test]
@@ -639,10 +710,15 @@ mod test {
639710
}
640711

641712
#[test]
642-
fn insert_string() {
713+
fn insert_get_string() {
643714
let mut bundle = PersistableBundle::new();
715+
644716
assert_eq!(bundle.insert_string("string", "foo"), Ok(()));
645-
assert_eq!(bundle.size(), 1);
717+
assert_eq!(bundle.insert_string("empty", ""), Ok(()));
718+
assert_eq!(bundle.size(), 2);
719+
720+
assert_eq!(bundle.get_string("string"), Ok(Some("foo".to_string())));
721+
assert_eq!(bundle.get_string("empty"), Ok(Some("".to_string())));
646722
}
647723

648724
#[test]

0 commit comments

Comments
 (0)