Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 07fb9c90 authored by Steven Moreland's avatar Steven Moreland
Browse files

libbinder_ndk: read/write string array

Bug: 111445392
Test: atest android.binder.cts
Change-Id: I75fd01d8e1c170aac2063b5c466767e111e5787a
parent 9ac9dcae
Loading
Loading
Loading
Loading
+78 −23
Original line number Diff line number Diff line
@@ -50,11 +50,51 @@ typedef struct AParcel AParcel;
 */
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 @@ typedef int32_t* (*AParcel_int32ArrayAllocator)(void* arrayData, size_t length);
/**
 * 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 @@ typedef uint32_t* (*AParcel_uint32ArrayAllocator)(void* arrayData, size_t length
/**
 * 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 @@ typedef int64_t* (*AParcel_int64ArrayAllocator)(void* arrayData, size_t length);
/**
 * 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 @@ typedef uint64_t* (*AParcel_uint64ArrayAllocator)(void* arrayData, size_t length
/**
 * 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 @@ typedef float* (*AParcel_floatArrayAllocator)(void* arrayData, size_t length);
/**
 * 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 float* (*AParcel_floatArrayAllocator)(void* arrayData, size_t length);
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 @@ typedef void (*AParcel_boolArraySetter)(void* arrayData, size_t index, bool valu
/**
 * 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 @@ typedef char16_t* (*AParcel_charArrayAllocator)(void* arrayData, size_t length);
/**
 * 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.
 *
@@ -163,16 +203,6 @@ typedef int8_t* (*AParcel_byteArrayAllocator)(void* arrayData, size_t length);

// @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.
 */
@@ -229,20 +259,45 @@ binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status
        __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
/**
+61 −6
Original line number Diff line number Diff line
@@ -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 @@ static inline T* AParcel_stdVectorAllocator(void* vectorData, size_t length) {
}

/**
 * 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_writeVector(AParcel* parcel, const std::vector<bo
 */
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>);
}

@@ -228,6 +236,32 @@ static inline char* AParcel_stdStringAllocator(void* stringData, size_t length)
    return &(*str)[0];
}

/**
 * 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.
 */
@@ -240,7 +274,28 @@ static inline binder_status_t AParcel_writeString(AParcel* parcel, const std::st
 */
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>
+2 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ LIBBINDER_NDK { # introduced=29
    AParcel_readParcelFileDescriptor;
    AParcel_readStatusHeader;
    AParcel_readString;
    AParcel_readStringArray;
    AParcel_readStrongBinder;
    AParcel_readUint32;
    AParcel_readUint32Array;
@@ -63,6 +64,7 @@ LIBBINDER_NDK { # introduced=29
    AParcel_writeParcelFileDescriptor;
    AParcel_writeStatusHeader;
    AParcel_writeString;
    AParcel_writeStringArray;
    AParcel_writeStrongBinder;
    AParcel_writeUint32;
    AParcel_writeUint32Array;
+65 −3
Original line number Diff line number Diff line
@@ -273,8 +273,8 @@ binder_status_t AParcel_writeString(AParcel* parcel, const char* string, size_t
    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 @@ binder_status_t AParcel_readString(const AParcel* parcel, AParcel_stringAllocato
        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 @@ binder_status_t AParcel_readString(const AParcel* parcel, AParcel_stringAllocato
    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
+3 −3
Original line number Diff line number Diff line
@@ -117,7 +117,7 @@ def main():

        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 @@ def main():
            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 @@ def main():
        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 + ">"]