libbinder_ndk: read/write string array

Bug: 111445392
Test: atest android.binder.cts
Change-Id: I75fd01d8e1c170aac2063b5c466767e111e5787a
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 0b5557f..a303f4a 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -50,11 +50,51 @@
  */
 void AParcel_delete(AParcel* parcel) __INTRODUCED_IN(29);
 
+/**
+ * This is called to allocate a buffer for a C-style string (null-terminated). The returned buffer
+ * should be at least length bytes. This includes space for a null terminator. length will always be
+ * strictly less than or equal to the maximum size that can be held in a size_t and will always be
+ * greater than 0.
+ *
+ * See also AParcel_readString.
+ *
+ * If allocation fails, null should be returned.
+ */
+typedef char* (*AParcel_stringAllocator)(void* stringData, size_t length);
+
+/**
+ * This is called to allocate an array of size 'length'.
+ *
+ * See also AParcel_readStringArray
+ */
+typedef bool (*AParcel_stringArrayAllocator)(void* arrayData, size_t length);
+
+/**
+ * This is called to allocate a string inside of an array that was allocated by an
+ * AParcel_stringArrayAllocator.
+ *
+ * The index returned will always be within the range [0, length of arrayData). The returned buffer
+ * should be at least length bytes. This includes space for a null-terminator. length will always be
+ * strictly less than or equal to the maximum size that can be held in a size_t and will always be
+ * greater than 0.
+ *
+ * See also AParcel_readStringArray
+ */
+typedef char* (*AParcel_stringArrayElementAllocator)(void* arrayData, size_t index, size_t length);
+
+/**
+ * This returns the length and buffer of an array at a specific index in an arrayData object.
+ *
+ * See also AParcel_writeStringArray
+ */
+typedef const char* (*AParcel_stringArrayElementGetter)(const void* arrayData, size_t index,
+                                                        size_t* outLength);
+
 // @START-PRIMITIVE-VECTOR-GETTERS
 /**
  * This is called to get the underlying data from an arrayData object.
  *
- * The implementation of this function should allocate a contiguous array of length length and
+ * The implementation of this function should allocate a contiguous array of size 'length' and
  * return that underlying buffer to be filled out. If there is an error or length is 0, null may be
  * returned.
  *
@@ -65,7 +105,7 @@
 /**
  * This is called to get the underlying data from an arrayData object.
  *
- * The implementation of this function should allocate a contiguous array of length length and
+ * The implementation of this function should allocate a contiguous array of size 'length' and
  * return that underlying buffer to be filled out. If there is an error or length is 0, null may be
  * returned.
  *
@@ -76,7 +116,7 @@
 /**
  * This is called to get the underlying data from an arrayData object.
  *
- * The implementation of this function should allocate a contiguous array of length length and
+ * The implementation of this function should allocate a contiguous array of size 'length' and
  * return that underlying buffer to be filled out. If there is an error or length is 0, null may be
  * returned.
  *
@@ -87,7 +127,7 @@
 /**
  * This is called to get the underlying data from an arrayData object.
  *
- * The implementation of this function should allocate a contiguous array of length length and
+ * The implementation of this function should allocate a contiguous array of size 'length' and
  * return that underlying buffer to be filled out. If there is an error or length is 0, null may be
  * returned.
  *
@@ -98,7 +138,7 @@
 /**
  * This is called to get the underlying data from an arrayData object.
  *
- * The implementation of this function should allocate a contiguous array of length length and
+ * The implementation of this function should allocate a contiguous array of size 'length' and
  * return that underlying buffer to be filled out. If there is an error or length is 0, null may be
  * returned.
  *
@@ -109,7 +149,7 @@
 /**
  * This is called to get the underlying data from an arrayData object.
  *
- * The implementation of this function should allocate a contiguous array of length length and
+ * The implementation of this function should allocate a contiguous array of size 'length' and
  * return that underlying buffer to be filled out. If there is an error or length is 0, null may be
  * returned.
  *
@@ -118,7 +158,7 @@
 typedef double* (*AParcel_doubleArrayAllocator)(void* arrayData, size_t length);
 
 /**
- * This allocates an array of length length inside of arrayData and returns whether or not there was
+ * This allocates an array of size 'length' inside of arrayData and returns whether or not there was
  * a success.
  *
  * See also AParcel_readBoolArray
@@ -142,7 +182,7 @@
 /**
  * This is called to get the underlying data from an arrayData object.
  *
- * The implementation of this function should allocate a contiguous array of length length and
+ * The implementation of this function should allocate a contiguous array of size 'length' and
  * return that underlying buffer to be filled out. If there is an error or length is 0, null may be
  * returned.
  *
@@ -153,7 +193,7 @@
 /**
  * This is called to get the underlying data from an arrayData object.
  *
- * The implementation of this function should allocate a contiguous array of length length and
+ * The implementation of this function should allocate a contiguous array of size 'length' and
  * return that underlying buffer to be filled out. If there is an error or length is 0, null may be
  * returned.
  *
@@ -164,16 +204,6 @@
 // @END-PRIMITIVE-VECTOR-GETTERS
 
 /**
- * This is called to allocate a buffer for a C-style string (null-terminated). The buffer should be
- * of length length which includes space for the null-terminator.
- *
- * See also AParcel_readString.
- *
- * If allocation fails, null should be returned.
- */
-typedef char* (*AParcel_stringAllocator)(void* stringData, size_t length);
-
-/**
  * Writes an AIBinder to the next location in a non-null parcel. Can be null.
  */
 binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) __INTRODUCED_IN(29);
@@ -229,20 +259,45 @@
         __INTRODUCED_IN(29);
 
 /**
- * Writes string value to the next location in a non-null parcel.
+ * Writes utf-8 string value to the next location in a non-null parcel.
  */
 binder_status_t AParcel_writeString(AParcel* parcel, const char* string, size_t length)
         __INTRODUCED_IN(29);
 
 /**
- * Reads and allocates string value from the next location in a non-null parcel.
+ * Reads and allocates utf-8 string value from the next location in a non-null parcel.
  *
  * Data is passed to the string allocator once the string size is known. This size includes the
  * space for the null-terminator of this string. This allocator returns a buffer which is used as
  * the output buffer from this read.
  */
-binder_status_t AParcel_readString(const AParcel* parcel, AParcel_stringAllocator allocator,
-                                   void* stringData) __INTRODUCED_IN(29);
+binder_status_t AParcel_readString(const AParcel* parcel, void* stringData,
+                                   AParcel_stringAllocator allocator) __INTRODUCED_IN(29);
+
+/**
+ * Writes utf-8 string array data to the next location in a non-null parcel.
+ *
+ * length is the length of the array. AParcel_stringArrayElementGetter will be called for all
+ * indices in range [0, length) with the arrayData provided here. The string length and buffer
+ * returned from this function will be used to fill out the data from the parcel.
+ */
+binder_status_t AParcel_writeStringArray(AParcel* parcel, const void* arrayData, size_t length,
+                                         AParcel_stringArrayElementGetter getter)
+        __INTRODUCED_IN(29);
+
+/**
+ * Reads and allocates utf-8 string array value from the next location in a non-null parcel.
+ *
+ * First, AParcel_stringArrayAllocator will be called with the size of the array to be read where
+ * length is the length of the array to be read from the parcel. Then, for each index i in [0,
+ * length), AParcel_stringArrayElementAllocator will be called with the length of the string to be
+ * read from the parcel. The resultant buffer from each of these calls will be filled according to
+ * the contents of the string that is read.
+ */
+binder_status_t AParcel_readStringArray(const AParcel* parcel, void* arrayData,
+                                        AParcel_stringArrayAllocator allocator,
+                                        AParcel_stringArrayElementAllocator elementAllocator)
+        __INTRODUCED_IN(29);
 
 // @START-PRIMITIVE-READ-WRITE
 /**
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
index 48902fd..3fcb121 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
@@ -36,7 +36,7 @@
 namespace ndk {
 
 /**
- * This retrieves and allocates a vector to length length and returns the underlying buffer.
+ * This retrieves and allocates a vector to size 'length' and returns the underlying buffer.
  */
 template <typename T>
 static inline T* AParcel_stdVectorAllocator(void* vectorData, size_t length) {
@@ -48,10 +48,18 @@
 }
 
 /**
- * This allocates a vector to length length and returns whether the allocation is successful.
+ * This allocates a vector to size 'length' and returns whether the allocation is successful.
+ *
+ * See also AParcel_stdVectorAllocator. Types used with this allocator have their sizes defined
+ * externally with respect to the NDK, and that size information is not passed into the NDK.
+ * Instead, it is used in cases where callbacks are used.
+ *
+ * See AParcel_readVector(const AParcel* parcel, std::vector<bool>)
+ * See AParcel_readVector(const AParcel* parcel, std::vector<std::string>)
  */
-static inline bool AParcel_stdVectorBoolAllocator(void* vectorData, size_t length) {
-    std::vector<bool>* vec = static_cast<std::vector<bool>*>(vectorData);
+template <typename T>
+static inline bool AParcel_stdVectorExternalAllocator(void* vectorData, size_t length) {
+    std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
     if (length > vec->max_size()) return false;
 
     vec->resize(length);
@@ -182,7 +190,7 @@
  */
 inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<bool>* vec) {
     void* vectorData = static_cast<void*>(vec);
-    return AParcel_readBoolArray(parcel, vectorData, AParcel_stdVectorBoolAllocator,
+    return AParcel_readBoolArray(parcel, vectorData, AParcel_stdVectorExternalAllocator<bool>,
                                  AParcel_stdVectorSetter<bool>);
 }
 
@@ -229,6 +237,32 @@
 }
 
 /**
+ * Allocates a std::string inside of a std::vector<std::string> at index index to size 'length'.
+ */
+static inline char* AParcel_stdVectorStringElementAllocator(void* vectorData, size_t index,
+                                                            size_t length) {
+    std::vector<std::string>* vec = static_cast<std::vector<std::string>*>(vectorData);
+
+    std::string& element = vec->at(index);
+    element.resize(length - 1);
+    return &element[0];
+}
+
+/**
+ * This gets the length and buffer of a std::string inside of a std::vector<std::string> at index
+ * index.
+ */
+static inline const char* AParcel_stdVectorStringElementGetter(const void* vectorData, size_t index,
+                                                               size_t* outLength) {
+    const std::vector<std::string>* vec = static_cast<const std::vector<std::string>*>(vectorData);
+
+    const std::string& element = vec->at(index);
+
+    *outLength = element.size();
+    return element.c_str();
+}
+
+/**
  * Convenience API for writing a std::string.
  */
 static inline binder_status_t AParcel_writeString(AParcel* parcel, const std::string& str) {
@@ -240,7 +274,28 @@
  */
 static inline binder_status_t AParcel_readString(const AParcel* parcel, std::string* str) {
     void* stringData = static_cast<void*>(str);
-    return AParcel_readString(parcel, AParcel_stdStringAllocator, stringData);
+    return AParcel_readString(parcel, stringData, AParcel_stdStringAllocator);
+}
+
+/**
+ * Convenience API for writing a std::vector<std::string>
+ */
+static inline binder_status_t AParcel_writeVector(AParcel* parcel,
+                                                  const std::vector<std::string>& vec) {
+    const void* vectorData = static_cast<const void*>(&vec);
+    return AParcel_writeStringArray(parcel, vectorData, vec.size(),
+                                    AParcel_stdVectorStringElementGetter);
+}
+
+/**
+ * Convenience API for reading a std::vector<std::string>
+ */
+static inline binder_status_t AParcel_readVector(const AParcel* parcel,
+                                                 std::vector<std::string>* vec) {
+    void* vectorData = static_cast<void*>(vec);
+    return AParcel_readStringArray(parcel, vectorData,
+                                   AParcel_stdVectorExternalAllocator<std::string>,
+                                   AParcel_stdVectorStringElementAllocator);
 }
 
 template <typename T>
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index ec6587a..d2c1a3d 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -41,6 +41,7 @@
     AParcel_readParcelFileDescriptor;
     AParcel_readStatusHeader;
     AParcel_readString;
+    AParcel_readStringArray;
     AParcel_readStrongBinder;
     AParcel_readUint32;
     AParcel_readUint32Array;
@@ -63,6 +64,7 @@
     AParcel_writeParcelFileDescriptor;
     AParcel_writeStatusHeader;
     AParcel_writeString;
+    AParcel_writeStringArray;
     AParcel_writeStrongBinder;
     AParcel_writeUint32;
     AParcel_writeUint32Array;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index d9f469ba..3b44a62 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -273,8 +273,8 @@
     return STATUS_OK;
 }
 
-binder_status_t AParcel_readString(const AParcel* parcel, AParcel_stringAllocator allocator,
-                                   void* stringData) {
+binder_status_t AParcel_readString(const AParcel* parcel, void* stringData,
+                                   AParcel_stringAllocator allocator) {
     size_t len16;
     const char16_t* str16 = parcel->get()->readString16Inplace(&len16);
 
@@ -291,7 +291,7 @@
         len8 = utf16_to_utf8_length(str16, len16) + 1;
     }
 
-    if (len8 <= 0 || len8 >= std::numeric_limits<int32_t>::max()) {
+    if (len8 <= 0 || len8 > std::numeric_limits<int32_t>::max()) {
         LOG(WARNING) << __func__ << ": Invalid string length: " << len8;
         return STATUS_BAD_VALUE;
     }
@@ -308,6 +308,68 @@
     return STATUS_OK;
 }
 
+binder_status_t AParcel_writeStringArray(AParcel* parcel, const void* arrayData, size_t length,
+                                         AParcel_stringArrayElementGetter getter) {
+    if (length > std::numeric_limits<int32_t>::max()) return STATUS_BAD_VALUE;
+
+    Parcel* rawParcel = parcel->get();
+
+    status_t status = rawParcel->writeInt32(static_cast<int32_t>(length));
+    if (status != STATUS_OK) return PruneStatusT(status);
+
+    for (size_t i = 0; i < length; i++) {
+        size_t length = 0;
+        const char* str = getter(arrayData, i, &length);
+        if (str == nullptr) return STATUS_BAD_VALUE;
+
+        binder_status_t status = AParcel_writeString(parcel, str, length);
+        if (status != STATUS_OK) return status;
+    }
+
+    return STATUS_OK;
+}
+
+// This implements AParcel_stringAllocator for a string using an array, index, and element
+// allocator.
+struct StringArrayElementAllocationAdapter {
+    void* arrayData; // stringData from the NDK
+    size_t index;    // index into the string array
+    AParcel_stringArrayElementAllocator elementAllocator;
+
+    static char* Allocator(void* stringData, size_t length) {
+        StringArrayElementAllocationAdapter* adapter =
+                static_cast<StringArrayElementAllocationAdapter*>(stringData);
+        return adapter->elementAllocator(adapter->arrayData, adapter->index, length);
+    }
+};
+
+binder_status_t AParcel_readStringArray(const AParcel* parcel, void* arrayData,
+                                        AParcel_stringArrayAllocator allocator,
+                                        AParcel_stringArrayElementAllocator elementAllocator) {
+    const Parcel* rawParcel = parcel->get();
+
+    int32_t length;
+    status_t status = rawParcel->readInt32(&length);
+
+    if (status != STATUS_OK) return PruneStatusT(status);
+    if (length < 0) return STATUS_UNEXPECTED_NULL;
+
+    if (!allocator(arrayData, length)) return STATUS_NO_MEMORY;
+
+    StringArrayElementAllocationAdapter adapter{
+            .arrayData = arrayData,
+            .index = 0,
+            .elementAllocator = elementAllocator,
+    };
+
+    for (; adapter.index < length; adapter.index++) {
+        AParcel_readString(parcel, static_cast<void*>(&adapter),
+                           StringArrayElementAllocationAdapter::Allocator);
+    }
+
+    return STATUS_OK;
+}
+
 // See gen_parcel_helper.py. These auto-generated read/write methods use the same types for
 // libbinder and this library.
 // @START
diff --git a/libs/binder/ndk/scripts/gen_parcel_helper.py b/libs/binder/ndk/scripts/gen_parcel_helper.py
index f3098e4..0e10220 100755
--- a/libs/binder/ndk/scripts/gen_parcel_helper.py
+++ b/libs/binder/ndk/scripts/gen_parcel_helper.py
@@ -117,7 +117,7 @@
 
         if nca:
             pre_header += "/**\n"
-            pre_header += " * This allocates an array of length length inside of arrayData and returns whether or not there was "
+            pre_header += " * This allocates an array of size 'length' inside of arrayData and returns whether or not there was "
             pre_header += "a success.\n"
             pre_header += " *\n"
             pre_header += " * See also " + read_func + "\n"
@@ -141,7 +141,7 @@
             pre_header += "/**\n"
             pre_header += " * This is called to get the underlying data from an arrayData object.\n"
             pre_header += " *\n"
-            pre_header += " * The implementation of this function should allocate a contiguous array of length length and "
+            pre_header += " * The implementation of this function should allocate a contiguous array of size 'length' and "
             pre_header += "return that underlying buffer to be filled out. If there is an error or length is 0, null may be "
             pre_header += "returned.\n"
             pre_header += " *\n"
@@ -192,7 +192,7 @@
         read_args += ["parcel"]
         read_args += ["vectorData"]
         if nca:
-            read_args += ["AParcel_stdVectorBoolAllocator"]
+            read_args += ["AParcel_stdVectorExternalAllocator<bool>"]
             read_args += ["AParcel_stdVectorSetter<" + cpp + ">"]
         else:
             read_args += ["AParcel_stdVectorAllocator<" + cpp + ">"]