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

Commit 3b31ccac authored by Jooyung Han's avatar Jooyung Han
Browse files

Support APIs for arrays of interfaces

libbinder/libbinder_ndk/libbinder_rs provides APIs to read/write array
of interfaces. For the Java backend, the AIDL compiler generate
read/write as inlined.

Bug: 203483658
Test: aidl_integration_test
Test: binder_parcel_fuzzer (a few mins)
Change-Id: I3b8c47df441d9cf9a8b8fdeee17ba4cd2eb3ea13
parent 1b072863
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -207,6 +207,23 @@ public:
    status_t            writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val) __attribute__((deprecated("use std::optional version instead")));
    status_t            writeStrongBinderVector(const std::vector<sp<IBinder>>& val);

    // Write an IInterface or a vector of IInterface's
    template <typename T,
              std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
    status_t writeStrongBinder(const sp<T>& val) {
        return writeStrongBinder(T::asBinder(val));
    }
    template <typename T,
              std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
    status_t writeStrongBinderVector(const std::vector<sp<T>>& val) {
        return writeData(val);
    }
    template <typename T,
              std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
    status_t writeStrongBinderVector(const std::optional<std::vector<sp<T>>>& val) {
        return writeData(val);
    }

    // Write an Enum vector with underlying type int8_t.
    // Does not use padding; each byte is contiguous.
    template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
@@ -421,6 +438,16 @@ public:
    status_t            readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const;
    status_t            readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const __attribute__((deprecated("use std::optional version instead")));
    status_t            readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
    template <typename T,
              std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
    status_t readStrongBinderVector(std::vector<sp<T>>* val) const {
        return readData(val);
    }
    template <typename T,
              std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
    status_t readStrongBinderVector(std::optional<std::vector<sp<T>>>* val) const {
        return readData(val);
    }

    status_t            readByteVector(std::optional<std::vector<int8_t>>* val) const;
    status_t            readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
+123 −67
Original line number Diff line number Diff line
@@ -27,15 +27,67 @@
#pragma once

#include <android/binder_auto_utils.h>
#include <android/binder_interface_utils.h>
#include <android/binder_internal_logging.h>
#include <android/binder_parcel.h>

#include <optional>
#include <string>
#include <type_traits>
#include <vector>

namespace ndk {

namespace {
template <typename Test, template <typename...> class Ref>
struct is_specialization : std::false_type {};

template <template <typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref> : std::true_type {};

template <typename Test, template <typename...> class Ref>
static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value;

// Get the first template type from a container, the T from MyClass<T, ...>.
template <typename T>
struct first_template_type {
    using type = void;
};

template <template <typename...> class V, typename T, typename... Args>
struct first_template_type<V<T, Args...>> {
    using type = T;
};

template <typename T>
using first_template_type_t = typename first_template_type<T>::type;

// Tells if T represents NDK interface (shared_ptr<ICInterface-derived>)
template <typename T>
static inline constexpr bool is_interface_v = is_specialization_v<T, std::shared_ptr>&&
        std::is_base_of_v<::ndk::ICInterface, first_template_type_t<T>>;

// Tells if T represents NDK parcelable with readFromParcel/writeToParcel methods defined
template <typename T, typename = void>
struct is_parcelable : std::false_type {};

template <typename T>
struct is_parcelable<
        T, std::void_t<decltype(std::declval<T>().readFromParcel(std::declval<const AParcel*>())),
                       decltype(std::declval<T>().writeToParcel(std::declval<AParcel*>()))>>
    : std::true_type {};

template <typename T>
static inline constexpr bool is_parcelable_v = is_parcelable<T>::value;

// Tells if T represents nullable NDK parcelable (optional<parcelable> or unique_ptr<parcelable>)
template <typename T>
static inline constexpr bool is_nullable_parcelable_v = is_parcelable_v<first_template_type_t<T>> &&
                                                        (is_specialization_v<T, std::optional> ||
                                                         is_specialization_v<T, std::unique_ptr>);

}  // namespace

/**
 * This retrieves and allocates a vector to size 'length' and returns the underlying buffer.
 */
@@ -429,18 +481,36 @@ static inline binder_status_t AParcel_readVector(
 */
template <typename P>
static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& p) {
    if constexpr (is_interface_v<P>) {
        if (!p) {
            return STATUS_UNEXPECTED_NULL;
        }
        return first_template_type_t<P>::writeToParcel(parcel, p);
    } else {
        static_assert(is_parcelable_v<P>);
        binder_status_t status = AParcel_writeInt32(parcel, 1);  // non-null
        if (status != STATUS_OK) {
            return status;
        }
        return p.writeToParcel(parcel);
    }
}

/**
 * Convenience API for reading a non-null parcelable.
 */
template <typename P>
static inline binder_status_t AParcel_readParcelable(const AParcel* parcel, P* p) {
    if constexpr (is_interface_v<P>) {
        binder_status_t status = first_template_type_t<P>::readFromParcel(parcel, p);
        if (status == STATUS_OK) {
            if (!*p) {
                return STATUS_UNEXPECTED_NULL;
            }
        }
        return status;
    } else {
        static_assert(is_parcelable_v<P>);
        int32_t null;
        binder_status_t status = AParcel_readInt32(parcel, &null);
        if (status != STATUS_OK) {
@@ -451,29 +521,17 @@ static inline binder_status_t AParcel_readParcelable(const AParcel* parcel, P* p
        }
        return p->readFromParcel(parcel);
    }

/**
 * Convenience API for writing a nullable parcelable.
 */
template <typename P>
static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel,
                                                              const std::optional<P>& p) {
    if (p == std::nullopt) {
        return AParcel_writeInt32(parcel, 0);  // null
    }
    binder_status_t status = AParcel_writeInt32(parcel, 1);  // non-null
    if (status != STATUS_OK) {
        return status;
    }
    return p->writeToParcel(parcel);
}

/**
 * Convenience API for writing a nullable parcelable.
 */
template <typename P>
static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel,
                                                              const std::unique_ptr<P>& p) {
static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel, const P& p) {
    if constexpr (is_interface_v<P>) {
        return first_template_type_t<P>::writeToParcel(parcel, p);
    } else {
        static_assert(is_nullable_parcelable_v<P>);
        if (!p) {
            return AParcel_writeInt32(parcel, 0);  // null
        }
@@ -483,13 +541,16 @@ static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel,
        }
        return p->writeToParcel(parcel);
    }
}

/**
 * Convenience API for reading a nullable parcelable.
 */
template <typename P>
static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel,
                                                             std::optional<P>* p) {
static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel, P* p) {
    if constexpr (is_interface_v<P>) {
        return first_template_type_t<P>::readFromParcel(parcel, p);
    } else if constexpr (is_specialization_v<P, std::optional>) {
        int32_t null;
        binder_status_t status = AParcel_readInt32(parcel, &null);
        if (status != STATUS_OK) {
@@ -499,16 +560,10 @@ static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parc
            *p = std::nullopt;
            return STATUS_OK;
        }
    *p = std::optional<P>(P{});
        *p = std::optional<first_template_type_t<P>>(first_template_type_t<P>{});
        return (*p)->readFromParcel(parcel);
}

/**
 * Convenience API for reading a nullable parcelable.
 */
template <typename P>
static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel,
                                                             std::unique_ptr<P>* p) {
    } else {
        static_assert(is_specialization_v<P, std::unique_ptr>);
        int32_t null;
        binder_status_t status = AParcel_readInt32(parcel, &null);
        if (status != STATUS_OK) {
@@ -518,9 +573,10 @@ static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parc
            p->reset();
            return STATUS_OK;
        }
    *p = std::make_unique<P>();
        *p = std::make_unique<first_template_type_t<P>>();
        return (*p)->readFromParcel(parcel);
    }
}

/**
 * Writes a parcelable object of type P inside a std::vector<P> at index 'index' to 'parcel'.
+4 −0
Original line number Diff line number Diff line
@@ -702,6 +702,8 @@ impl<T: SerializeOption + FromIBinder + ?Sized> SerializeOption for Strong<T> {
    }
}

impl<T: Serialize + FromIBinder + ?Sized> SerializeArray for Strong<T> {}

impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> {
    fn deserialize(parcel: &Parcel) -> Result<Self> {
        let ibinder: SpIBinder = parcel.read()?;
@@ -716,6 +718,8 @@ impl<T: FromIBinder + ?Sized> DeserializeOption for Strong<T> {
    }
}

impl<T: FromIBinder + ?Sized> DeserializeArray for Strong<T> {}

// We need these to support Option<&T> for all T
impl<T: Serialize + ?Sized> Serialize for &T {
    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+2 −0
Original line number Diff line number Diff line
@@ -192,6 +192,8 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS {
    // only reading one binder type for now
    PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder),
    PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readNullableStrongBinder),
    PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::os::IServiceManager>>, readStrongBinderVector),
    PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::sp<android::os::IServiceManager>>>, readStrongBinderVector),

    PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
    PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
+38 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
// TODO(b/142061461): parent class
class SomeParcelable {
public:
    binder_status_t writeToParcel(AParcel* /*parcel*/) { return STATUS_OK; }
    binder_status_t readFromParcel(const AParcel* parcel) {
        return AParcel_readInt32(parcel, &mValue);
    }
@@ -33,6 +34,41 @@ private:
    int32_t mValue = 0;
};

class ISomeInterface : public ::ndk::ICInterface {
public:
    ISomeInterface() = default;
    virtual ~ISomeInterface() = default;
    static binder_status_t readFromParcel(const AParcel* parcel,
                                          std::shared_ptr<ISomeInterface>* instance);
};

static binder_status_t onTransact(AIBinder*, transaction_code_t, const AParcel*, AParcel*) {
    return STATUS_UNKNOWN_TRANSACTION;
}

static AIBinder_Class* g_class = ::ndk::ICInterface::defineClass("ISomeInterface", onTransact);

class BpSomeInterface : public ::ndk::BpCInterface<ISomeInterface> {
public:
    explicit BpSomeInterface(const ::ndk::SpAIBinder& binder) : BpCInterface(binder) {}
    virtual ~BpSomeInterface() = default;
};

binder_status_t ISomeInterface::readFromParcel(const AParcel* parcel,
                                               std::shared_ptr<ISomeInterface>* instance) {
    ::ndk::SpAIBinder binder;
    binder_status_t status = AParcel_readStrongBinder(parcel, binder.getR());
    if (status == STATUS_OK) {
        if (AIBinder_associateClass(binder.get(), g_class)) {
            *instance = std::static_pointer_cast<ISomeInterface>(
                    ::ndk::ICInterface::asInterface(binder.get()));
        } else {
            *instance = ::ndk::SharedRefBase::make<BpSomeInterface>(binder);
        }
    }
    return status;
}

#define PARCEL_READ(T, FUN)                                              \
    [](const NdkParcelAdapter& p, uint8_t /*data*/) {                    \
        FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \
@@ -100,6 +136,8 @@ std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{
        PARCEL_READ(std::optional<std::vector<ndk::SpAIBinder>>, ndk::AParcel_readVector),
        PARCEL_READ(std::vector<ndk::ScopedFileDescriptor>, ndk::AParcel_readVector),
        PARCEL_READ(std::optional<std::vector<ndk::ScopedFileDescriptor>>, ndk::AParcel_readVector),
        PARCEL_READ(std::vector<std::shared_ptr<ISomeInterface>>, ndk::AParcel_readVector),
        PARCEL_READ(std::optional<std::vector<std::shared_ptr<ISomeInterface>>>, ndk::AParcel_readVector),
        PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector),
        PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector),
        PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector),