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 + ">"]