libbinder_ndk: read/write array for primitive types.
type | contiguous in NDK API | contiguous on the wire
--------------------------------------------------------
bool | no* | no
char | yes | no**
other | yes | yes
* std::vector<bool> as an implementation detail stores a packed byte
array. This packed byte array is not exposed as an implementation
detail, so even if we made bool contiguous on the wire, we couldn't
ever make it contiguous in the NDK API.
** read/write Int32/Int is called repeatedly which writes each
char16_t into a 32-bit wide section on the wire.
Arrays in Java AIDL are also very annoying. Whereas in C++, their size
can be changed, in Java, because of the way out-parameters work, the
array size cannot be changed. We might consider adding a better error
when this happens.
Bug: 111445392
Test: atest android.binder.cts
Change-Id: I3934f7d5cd749810c4a512ac3fb57b63f9ae4ea5
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 0e97b50..73df69c 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -51,6 +51,99 @@
void AParcel_delete(AParcel* parcel) __INTRODUCED_IN(29);
/**
+ * This is called to allocate an array with a given length. If allocation fails, null should be
+ * returned.
+ */
+typedef void* (*AParcel_arrayReallocator)(void* vectorData, size_t length);
+
+// @START-PRIMITIVE-VECTOR-GETTERS
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef int32_t* (*AParcel_int32ArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef uint32_t* (*AParcel_uint32ArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef int64_t* (*AParcel_int64ArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef uint64_t* (*AParcel_uint64ArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef float* (*AParcel_floatArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef double* (*AParcel_doubleArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef bool (*AParcel_boolArrayGetter)(const void* arrayData, size_t index);
+
+/**
+ * This is called to set an underlying value in an arrayData object at index.
+ */
+typedef void (*AParcel_boolArraySetter)(void* arrayData, size_t index, bool value);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef char16_t* (*AParcel_charArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef int8_t* (*AParcel_byteArrayGetter)(void* arrayData);
+
+// @END-PRIMITIVE-VECTOR-GETTERS
+
+/**
+ * This is called to allocate a buffer
+ *
+ * The length here includes the space required to insert a '\0' for a properly formed c-str. If the
+ * buffer returned from this function is retStr, it will be filled by AParcel_readString with the
+ * data from the remote process, and it will be filled such that retStr[length] == '\0'.
+ *
+ * If allocation fails, null should be returned.
+ */
+typedef void* (*AParcel_string_reallocator)(void* stringData, size_t length);
+
+/**
+ * This is called to get the buffer from a stringData object.
+ */
+typedef char* (*AParcel_string_getter)(void* stringData);
+
+/**
* 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);
@@ -95,22 +188,6 @@
__INTRODUCED_IN(29);
/**
- * This is called to allocate a buffer
- *
- * The length here includes the space required to insert a '\0' for a properly formed c-str. If the
- * buffer returned from this function is retStr, it will be filled by AParcel_readString with the
- * data from the remote process, and it will be filled such that retStr[length] == '\0'.
- *
- * If allocation fails, null should be returned.
- */
-typedef void* (*AParcel_string_reallocator)(void* stringData, size_t length);
-
-/**
- * This is called to get the buffer from a stringData object.
- */
-typedef char* (*AParcel_string_getter)(void* stringData);
-
-/**
* Reads and allocates 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 data should be used to
@@ -125,7 +202,7 @@
AParcel_string_getter getter, void** stringData)
__INTRODUCED_IN(29);
-// @START
+// @START-PRIMITIVE-READ-WRITE
/**
* Writes int32_t value to the next location in a non-null parcel.
*/
@@ -216,7 +293,125 @@
*/
binder_status_t AParcel_readByte(const AParcel* parcel, int8_t* value) __INTRODUCED_IN(29);
-// @END
+/**
+ * Writes an array of int32_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of uint32_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of int64_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of uint64_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of float to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of double to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of bool to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData,
+ AParcel_boolArrayGetter getter, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of char16_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of int8_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of int32_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readInt32Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_int32ArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of uint32_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readUint32Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_uint32ArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of int64_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readInt64Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_int64ArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of uint64_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readUint64Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_uint64ArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of float from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readFloatArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_floatArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of double from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readDoubleArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_doubleArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of bool from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readBoolArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_boolArraySetter setter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of char16_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readCharArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_charArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of int8_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readByteArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_byteArrayGetter getter) __INTRODUCED_IN(29);
+
+// @END-PRIMITIVE-READ-WRITE
#endif //__ANDROID_API__ >= __ANDROID_API_Q__
__END_DECLS
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 d3e6cae..6341b46 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
@@ -31,10 +31,242 @@
#ifdef __cplusplus
#include <string>
+#include <vector>
namespace ndk {
/**
+ * This resizes a std::vector of some underlying type to the given length.
+ */
+template <typename T>
+static inline void* AParcel_stdVectorReallocator(void* vectorData, size_t length) {
+ std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
+ if (length > vec->max_size()) return nullptr;
+
+ vec->resize(length);
+ return vec;
+}
+
+/**
+ * This retrieves the underlying contiguous vector from a corresponding vectorData.
+ */
+template <typename T>
+static inline T* AParcel_stdVectorGetter(void* vectorData) {
+ std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
+ return vec->data();
+}
+
+/**
+ * This retrieves the underlying value in a vector which may not be contiguous at index from a
+ * corresponding vectorData.
+ */
+template <typename T>
+static inline T AParcel_stdVectorGetter(const void* vectorData, size_t index) {
+ const std::vector<T>* vec = static_cast<const std::vector<T>*>(vectorData);
+ return (*vec)[index];
+}
+
+/**
+ * This sets the underlying value in a corresponding vectorData which may not be contiguous at
+ * index.
+ */
+template <typename T>
+static inline void AParcel_stdVectorSetter(void* vectorData, size_t index, T value) {
+ std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
+ (*vec)[index] = value;
+}
+
+/**
+ * Writes a vector to the next location in a non-null parcel.
+ */
+template <typename T>
+static inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<T>& vec);
+
+/**
+ * Reads a vector to the next location in a non-null parcel.
+ */
+template <typename T>
+static inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<T>* vec);
+
+// @START
+/**
+ * Writes a vector of int32_t to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<int32_t>(AParcel* parcel,
+ const std::vector<int32_t>& vec) {
+ return AParcel_writeInt32Array(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of int32_t from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<int32_t>(const AParcel* parcel,
+ std::vector<int32_t>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readInt32Array(parcel, &vectorData, &AParcel_stdVectorReallocator<int32_t>,
+ AParcel_stdVectorGetter<int32_t>);
+}
+
+/**
+ * Writes a vector of uint32_t to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<uint32_t>(AParcel* parcel,
+ const std::vector<uint32_t>& vec) {
+ return AParcel_writeUint32Array(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of uint32_t from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<uint32_t>(const AParcel* parcel,
+ std::vector<uint32_t>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readUint32Array(parcel, &vectorData, &AParcel_stdVectorReallocator<uint32_t>,
+ AParcel_stdVectorGetter<uint32_t>);
+}
+
+/**
+ * Writes a vector of int64_t to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<int64_t>(AParcel* parcel,
+ const std::vector<int64_t>& vec) {
+ return AParcel_writeInt64Array(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of int64_t from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<int64_t>(const AParcel* parcel,
+ std::vector<int64_t>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readInt64Array(parcel, &vectorData, &AParcel_stdVectorReallocator<int64_t>,
+ AParcel_stdVectorGetter<int64_t>);
+}
+
+/**
+ * Writes a vector of uint64_t to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<uint64_t>(AParcel* parcel,
+ const std::vector<uint64_t>& vec) {
+ return AParcel_writeUint64Array(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of uint64_t from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<uint64_t>(const AParcel* parcel,
+ std::vector<uint64_t>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readUint64Array(parcel, &vectorData, &AParcel_stdVectorReallocator<uint64_t>,
+ AParcel_stdVectorGetter<uint64_t>);
+}
+
+/**
+ * Writes a vector of float to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<float>(AParcel* parcel, const std::vector<float>& vec) {
+ return AParcel_writeFloatArray(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of float from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<float>(const AParcel* parcel, std::vector<float>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readFloatArray(parcel, &vectorData, &AParcel_stdVectorReallocator<float>,
+ AParcel_stdVectorGetter<float>);
+}
+
+/**
+ * Writes a vector of double to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<double>(AParcel* parcel,
+ const std::vector<double>& vec) {
+ return AParcel_writeDoubleArray(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of double from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<double>(const AParcel* parcel, std::vector<double>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readDoubleArray(parcel, &vectorData, &AParcel_stdVectorReallocator<double>,
+ AParcel_stdVectorGetter<double>);
+}
+
+/**
+ * Writes a vector of bool to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<bool>(AParcel* parcel, const std::vector<bool>& vec) {
+ return AParcel_writeBoolArray(parcel, static_cast<const void*>(&vec),
+ AParcel_stdVectorGetter<bool>, vec.size());
+}
+
+/**
+ * Reads a vector of bool from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<bool>(const AParcel* parcel, std::vector<bool>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readBoolArray(parcel, &vectorData, &AParcel_stdVectorReallocator<bool>,
+ AParcel_stdVectorSetter<bool>);
+}
+
+/**
+ * Writes a vector of char16_t to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<char16_t>(AParcel* parcel,
+ const std::vector<char16_t>& vec) {
+ return AParcel_writeCharArray(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of char16_t from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<char16_t>(const AParcel* parcel,
+ std::vector<char16_t>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readCharArray(parcel, &vectorData, &AParcel_stdVectorReallocator<char16_t>,
+ AParcel_stdVectorGetter<char16_t>);
+}
+
+/**
+ * Writes a vector of int8_t to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<int8_t>(AParcel* parcel,
+ const std::vector<int8_t>& vec) {
+ return AParcel_writeByteArray(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of int8_t from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<int8_t>(const AParcel* parcel, std::vector<int8_t>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readByteArray(parcel, &vectorData, &AParcel_stdVectorReallocator<int8_t>,
+ AParcel_stdVectorGetter<int8_t>);
+}
+
+// @END
+
+/**
* Takes a std::string and reallocates it to the specified length. For use with AParcel_readString.
* See use below in AParcel_readString.
*/
@@ -68,6 +300,27 @@
&stringData);
}
+template <typename T>
+static inline binder_status_t AParcel_writeVectorSize(AParcel* parcel, const std::vector<T>& vec) {
+ if (vec.size() > INT32_MAX) {
+ return STATUS_BAD_VALUE;
+ }
+
+ return AParcel_writeInt32(parcel, static_cast<int32_t>(vec.size()));
+}
+
+template <typename T>
+static inline binder_status_t AParcel_resizeVector(const AParcel* parcel, std::vector<T>* vec) {
+ int32_t size;
+ binder_status_t err = AParcel_readInt32(parcel, &size);
+
+ if (err != STATUS_OK) return err;
+ if (size < 0) return STATUS_UNEXPECTED_NULL;
+
+ vec->resize(static_cast<size_t>(size));
+ return STATUS_OK;
+}
+
} // namespace ndk
#endif // __cplusplus
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 2a1bff1..f84814f 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -24,30 +24,48 @@
AIBinder_Weak_promote;
AParcel_delete;
AParcel_readBool;
+ AParcel_readBoolArray;
AParcel_readByte;
+ AParcel_readByteArray;
AParcel_readChar;
+ AParcel_readCharArray;
AParcel_readDouble;
+ AParcel_readDoubleArray;
AParcel_readFloat;
+ AParcel_readFloatArray;
AParcel_readInt32;
+ AParcel_readInt32Array;
AParcel_readInt64;
+ AParcel_readInt64Array;
AParcel_readNullableStrongBinder;
AParcel_readStatusHeader;
AParcel_readString;
AParcel_readStrongBinder;
AParcel_readUint32;
+ AParcel_readUint32Array;
AParcel_readUint64;
+ AParcel_readUint64Array;
AParcel_writeBool;
+ AParcel_writeBoolArray;
AParcel_writeByte;
+ AParcel_writeByteArray;
AParcel_writeChar;
+ AParcel_writeCharArray;
AParcel_writeDouble;
+ AParcel_writeDoubleArray;
AParcel_writeFloat;
+ AParcel_writeFloatArray;
AParcel_writeInt32;
+ AParcel_writeInt32Array;
AParcel_writeInt64;
+ AParcel_writeInt64Array;
AParcel_writeStatusHeader;
AParcel_writeString;
AParcel_writeStrongBinder;
AParcel_writeUint32;
+ AParcel_writeUint32Array;
AParcel_writeUint64;
+ AParcel_writeUint64Array;
AStatus_delete;
AStatus_fromExceptionCode;
AStatus_fromExceptionCodeWithMessage;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 3e03e90..5279b54 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -31,6 +31,163 @@
using ::android::sp;
using ::android::status_t;
+template <typename T>
+using ContiguousArrayGetter = T* (*)(void* arrayData);
+template <typename T>
+using ArrayGetter = T (*)(const void* arrayData, size_t index);
+template <typename T>
+using ArraySetter = void (*)(void* arrayData, size_t index, T value);
+
+template <typename T>
+binder_status_t WriteArray(AParcel* parcel, const T* array, size_t length) {
+ 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);
+
+ int32_t size = 0;
+ if (__builtin_smul_overflow(sizeof(T), length, &size)) return STATUS_NO_MEMORY;
+
+ void* const data = rawParcel->writeInplace(size);
+ if (data == nullptr) return STATUS_NO_MEMORY;
+
+ memcpy(data, array, size);
+
+ return STATUS_OK;
+}
+
+// Each element in a char16_t array is converted to an int32_t (not packed).
+template <>
+binder_status_t WriteArray<char16_t>(AParcel* parcel, const char16_t* array, size_t length) {
+ 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);
+
+ int32_t size = 0;
+ if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY;
+
+ for (int32_t i = 0; i < length; i++) {
+ status = rawParcel->writeChar(array[i]);
+
+ if (status != STATUS_OK) return PruneStatusT(status);
+ }
+
+ return STATUS_OK;
+}
+
+template <typename T>
+binder_status_t ReadArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator, ContiguousArrayGetter<T> getter) {
+ 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;
+
+ *arrayData = reallocator(*arrayData, length);
+ if (*arrayData == nullptr) return STATUS_NO_MEMORY;
+
+ if (length == 0) return STATUS_OK;
+
+ T* array = getter(*arrayData);
+ if (array == nullptr) return STATUS_NO_MEMORY;
+
+ int32_t size = 0;
+ if (__builtin_smul_overflow(sizeof(T), length, &size)) return STATUS_NO_MEMORY;
+
+ const void* data = rawParcel->readInplace(size);
+ if (data == nullptr) return STATUS_NO_MEMORY;
+
+ memcpy(array, data, size);
+
+ return STATUS_OK;
+}
+
+// Each element in a char16_t array is converted to an int32_t (not packed)
+template <>
+binder_status_t ReadArray<char16_t>(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ ContiguousArrayGetter<char16_t> getter) {
+ 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;
+
+ *arrayData = reallocator(*arrayData, length);
+ if (*arrayData == nullptr) return STATUS_NO_MEMORY;
+
+ if (length == 0) return STATUS_OK;
+
+ char16_t* array = getter(*arrayData);
+ if (array == nullptr) return STATUS_NO_MEMORY;
+
+ int32_t size = 0;
+ if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY;
+
+ for (int32_t i = 0; i < length; i++) {
+ status = rawParcel->readChar(array + i);
+
+ if (status != STATUS_OK) return PruneStatusT(status);
+ }
+
+ return STATUS_OK;
+}
+
+template <typename T>
+binder_status_t WriteArray(AParcel* parcel, const void* arrayData, ArrayGetter<T> getter,
+ size_t length, status_t (Parcel::*write)(T)) {
+ 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++) {
+ status = (rawParcel->*write)(getter(arrayData, i));
+
+ if (status != STATUS_OK) return PruneStatusT(status);
+ }
+
+ return STATUS_OK;
+}
+
+template <typename T>
+binder_status_t ReadArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator, ArraySetter<T> setter,
+ status_t (Parcel::*read)(T*) const) {
+ 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;
+
+ *arrayData = reallocator(*arrayData, length);
+ if (*arrayData == nullptr) return STATUS_NO_MEMORY;
+
+ for (size_t i = 0; i < length; i++) {
+ T readTarget;
+ status = (rawParcel->*read)(&readTarget);
+ if (status != STATUS_OK) return PruneStatusT(status);
+
+ setter(*arrayData, i, readTarget);
+ }
+
+ return STATUS_OK;
+}
+
void AParcel_delete(AParcel* parcel) {
delete parcel;
}
@@ -122,6 +279,11 @@
}
*stringData = reallocator(*stringData, len8);
+
+ if (*stringData == nullptr) {
+ return STATUS_NO_MEMORY;
+ }
+
char* str8 = getter(*stringData);
if (str8 == nullptr) {
@@ -227,4 +389,95 @@
return PruneStatusT(status);
}
+binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* value, size_t length) {
+ return WriteArray<int32_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* value, size_t length) {
+ return WriteArray<uint32_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* value, size_t length) {
+ return WriteArray<int64_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* value, size_t length) {
+ return WriteArray<uint64_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* value, size_t length) {
+ return WriteArray<float>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* value, size_t length) {
+ return WriteArray<double>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData,
+ AParcel_boolArrayGetter getter, size_t length) {
+ return WriteArray<bool>(parcel, arrayData, getter, length, &Parcel::writeBool);
+}
+
+binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* value, size_t length) {
+ return WriteArray<char16_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* value, size_t length) {
+ return WriteArray<int8_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_readInt32Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_int32ArrayGetter getter) {
+ return ReadArray<int32_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readUint32Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_uint32ArrayGetter getter) {
+ return ReadArray<uint32_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readInt64Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_int64ArrayGetter getter) {
+ return ReadArray<int64_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readUint64Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_uint64ArrayGetter getter) {
+ return ReadArray<uint64_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readFloatArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_floatArrayGetter getter) {
+ return ReadArray<float>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readDoubleArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_doubleArrayGetter getter) {
+ return ReadArray<double>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readBoolArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_boolArraySetter setter) {
+ return ReadArray<bool>(parcel, arrayData, reallocator, setter, &Parcel::readBool);
+}
+
+binder_status_t AParcel_readCharArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_charArrayGetter getter) {
+ return ReadArray<char16_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readByteArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_byteArrayGetter getter) {
+ return ReadArray<int8_t>(parcel, arrayData, reallocator, getter);
+}
+
// @END
diff --git a/libs/binder/ndk/scripts/gen_parcel_helper.py b/libs/binder/ndk/scripts/gen_parcel_helper.py
index bbd3e5d..45f8d06 100755
--- a/libs/binder/ndk/scripts/gen_parcel_helper.py
+++ b/libs/binder/ndk/scripts/gen_parcel_helper.py
@@ -30,13 +30,15 @@
("Byte", "int8_t"),
]
-def replaceFileTags(path, content):
+non_contiguously_addressable = {"Bool"}
+
+def replaceFileTags(path, content, start_tag, end_tag):
print("Updating", path)
with open(path, "r+") as f:
lines = f.readlines()
- start = lines.index("// @START\n")
- end = lines.index("// @END\n")
+ start = lines.index("// @" + start_tag + "\n")
+ end = lines.index("// @" + end_tag + "\n")
if end <= start or start < 0 or end < 0:
print("Failed to find tags in", path)
@@ -59,8 +61,10 @@
print("Updating auto-generated code")
+ pre_header = ""
header = ""
source = ""
+ cpp_helper = ""
for pretty, cpp in data_types:
header += "/**\n"
@@ -82,8 +86,97 @@
source += " return PruneStatusT(status);\n"
source += "}\n\n"
- replaceFileTags(ROOT + "include_ndk/android/binder_parcel.h", header)
- replaceFileTags(ROOT + "parcel.cpp", source)
+ for pretty, cpp in data_types:
+ nca = pretty in non_contiguously_addressable
+
+ arg_type = "const " + cpp + "* value"
+ if nca: arg_type = "const void* arrayData, AParcel_" + pretty.lower() + "ArrayGetter getter"
+ args = "value, length"
+ if nca: args = "arrayData, getter, length, &Parcel::write" + pretty
+
+ header += "/**\n"
+ header += " * Writes an array of " + cpp + " to the next location in a non-null parcel.\n"
+ header += " */\n"
+ header += "binder_status_t AParcel_write" + pretty + "Array(AParcel* parcel, " + arg_type + ", size_t length) __INTRODUCED_IN(29);\n\n"
+ source += "binder_status_t AParcel_write" + pretty + "Array(AParcel* parcel, " + arg_type + ", size_t length) {\n"
+ source += " return WriteArray<" + cpp + ">(parcel, " + args + ");\n";
+ source += "}\n\n"
+
+ for pretty, cpp in data_types:
+ nca = pretty in non_contiguously_addressable
+
+ extra_getter_args = ""
+ if nca: extra_getter_args = ", size_t index"
+ getter_return = cpp + "*"
+ if nca: getter_return = cpp
+ getter_array_data = "void* arrayData"
+ if nca: getter_array_data = "const void* arrayData"
+
+ getter_type = "AParcel_" + pretty.lower() + "ArrayGetter"
+ setter_type = "AParcel_" + pretty.lower() + "ArraySetter"
+
+ pre_header += "/**\n"
+ pre_header += " * This is called to get the underlying data from an arrayData object.\n"
+ pre_header += " *\n"
+ pre_header += " * This will never be called for an empty array.\n"
+ pre_header += " */\n"
+ pre_header += "typedef " + getter_return + " (*" + getter_type + ")(" + getter_array_data + extra_getter_args + ");\n\n"
+
+ if nca:
+ pre_header += "/**\n"
+ pre_header += " * This is called to set an underlying value in an arrayData object at index.\n"
+ pre_header += " */\n"
+ pre_header += "typedef void (*" + setter_type + ")(void* arrayData, size_t index, " + cpp + " value);\n\n"
+
+ read_using = "getter"
+ if nca: read_using = "setter"
+ read_type = getter_type
+ if nca: read_type = setter_type
+
+ arguments = ["const AParcel* parcel"]
+ arguments += ["void** arrayData"]
+ arguments += ["AParcel_arrayReallocator reallocator"]
+ arguments += [read_type + " " + read_using]
+ arguments = ", ".join(arguments)
+
+ header += "/**\n"
+ header += " * Reads an array of " + cpp + " from the next location in a non-null parcel.\n"
+ header += " */\n"
+ header += "binder_status_t AParcel_read" + pretty + "Array(" + arguments + ") __INTRODUCED_IN(29);\n\n"
+ source += "binder_status_t AParcel_read" + pretty + "Array(" + arguments + ") {\n"
+ additional_args = ""
+ if nca: additional_args = ", &Parcel::read" + pretty
+ source += " return ReadArray<" + cpp + ">(parcel, arrayData, reallocator, " + read_using + additional_args + ");\n";
+ source += "}\n\n"
+
+ cpp_helper += "/**\n"
+ cpp_helper += " * Writes a vector of " + cpp + " to the next location in a non-null parcel.\n"
+ cpp_helper += " */\n"
+ cpp_helper += "template<>\n"
+ cpp_helper += "inline binder_status_t AParcel_writeVector<" + cpp + ">(AParcel* parcel, const std::vector<" + cpp + ">& vec) {\n"
+ write_args = "vec.data()"
+ if nca: write_args = "static_cast<const void*>(&vec), AParcel_stdVectorGetter<" + cpp + ">"
+ cpp_helper += " return AParcel_write" + pretty + "Array(parcel, " + write_args + ", vec.size());\n"
+ cpp_helper += "}\n\n"
+
+ cpp_helper += "/**\n"
+ cpp_helper += " * Reads a vector of " + cpp + " from the next location in a non-null parcel.\n"
+ cpp_helper += " */\n"
+ cpp_helper += "template<>\n"
+ cpp_helper += "inline binder_status_t AParcel_readVector<" + cpp + ">(const AParcel* parcel, std::vector<" + cpp + ">* vec) {\n"
+ cpp_helper += " void* vectorData = static_cast<void*>(vec);\n"
+ read_args = []
+ read_args += ["parcel"]
+ read_args += ["&vectorData"]
+ read_args += ["&AParcel_stdVectorReallocator<" + cpp + ">"]
+ read_args += ["AParcel_stdVector" + read_using.capitalize() + "<" + cpp + ">"]
+ cpp_helper += " return AParcel_read" + pretty + "Array(" + ", ".join(read_args) + ");\n"
+ cpp_helper += "}\n\n"
+
+ replaceFileTags(ROOT + "include_ndk/android/binder_parcel.h", pre_header, "START-PRIMITIVE-VECTOR-GETTERS", "END-PRIMITIVE-VECTOR-GETTERS")
+ replaceFileTags(ROOT + "include_ndk/android/binder_parcel.h", header, "START-PRIMITIVE-READ-WRITE", "END-PRIMITIVE-READ-WRITE")
+ replaceFileTags(ROOT + "parcel.cpp", source, "START", "END")
+ replaceFileTags(ROOT + "include_ndk/android/binder_parcel_utils.h", cpp_helper, "START", "END")
print("Updating DONE.")
diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/main_client.cpp
index 22bf1e5..6945cac 100644
--- a/libs/binder/ndk/test/main_client.cpp
+++ b/libs/binder/ndk/test/main_client.cpp
@@ -134,3 +134,4 @@
#include <android/binder_auto_utils.h>
#include <android/binder_interface_utils.h>
+#include <android/binder_parcel_utils.h>
diff --git a/libs/binder/ndk/update.sh b/libs/binder/ndk/update.sh
index 49b4730..9a4577f 100755
--- a/libs/binder/ndk/update.sh
+++ b/libs/binder/ndk/update.sh
@@ -18,6 +18,6 @@
set -ex
# This script makes sure that the source code is in sync with the various scripts
-./scripts/init_map.sh > libbinder_ndk.map.txt
./scripts/gen_parcel_helper.py
./scripts/format.sh
+./scripts/init_map.sh > libbinder_ndk.map.txt