Merge "Add method to get vector of strings." into main am: 7671143194 am: 99be628cf6

Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/3457319

Change-Id: I3a5997c28bfec5de60a16acd7c59b93fd0c732ec
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/libs/binder/rust/src/persistable_bundle.rs b/libs/binder/rust/src/persistable_bundle.rs
index 46d0fe7..e1d64ad 100644
--- a/libs/binder/rust/src/persistable_bundle.rs
+++ b/libs/binder/rust/src/persistable_bundle.rs
@@ -26,16 +26,16 @@
     APersistableBundle_getDouble, APersistableBundle_getDoubleVector, APersistableBundle_getInt,
     APersistableBundle_getIntVector, APersistableBundle_getLong, APersistableBundle_getLongVector,
     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_getStringVector, 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 std::ffi::{c_char, c_void, CString, NulError};
+use std::ffi::{c_char, c_void, CStr, CString, NulError};
 use std::ptr::{null_mut, slice_from_raw_parts_mut, NonNull};
 use zerocopy::FromZeros;
 
@@ -438,9 +438,10 @@
     /// 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 + Default>(
+    unsafe fn get_vec<T: Clone>(
         &self,
         key: &str,
+        default: T,
         get_func: unsafe extern "C" fn(
             *const APersistableBundle,
             *const c_char,
@@ -454,9 +455,12 @@
         // 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![
-                    T::default();
+                    default;
                     usize::try_from(required_buffer_size).expect(
                         "APersistableBundle_get*Vector returned invalid size"
                     ) / size_of::<T>()
@@ -476,6 +480,9 @@
                     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)),
                 }
             }
@@ -489,7 +496,7 @@
     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, APersistableBundle_getBooleanVector) }
+        unsafe { self.get_vec(key, Default::default(), APersistableBundle_getBooleanVector) }
     }
 
     /// Gets the i32 vector value associated with the given key.
@@ -499,7 +506,7 @@
     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, APersistableBundle_getIntVector) }
+        unsafe { self.get_vec(key, Default::default(), APersistableBundle_getIntVector) }
     }
 
     /// Gets the i64 vector value associated with the given key.
@@ -509,7 +516,7 @@
     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, APersistableBundle_getLongVector) }
+        unsafe { self.get_vec(key, Default::default(), APersistableBundle_getLongVector) }
     }
 
     /// Gets the f64 vector value associated with the given key.
@@ -519,7 +526,45 @@
     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, APersistableBundle_getDoubleVector) }
+        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.
@@ -545,6 +590,36 @@
     }
 }
 
+/// 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 {}
 
@@ -613,7 +688,7 @@
 ///
 /// # Safety
 ///
-/// `context` must point to a `usize` to which we can write.
+/// `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();
@@ -621,9 +696,12 @@
     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;
+    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()
 }
@@ -751,6 +829,10 @@
         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]