Merge "libbinder: Parcel APIs for fixed-size arrays"
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 9670d7b..8dbdc1d 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <array>
 #include <map> // for legacy reasons
 #include <string>
 #include <type_traits>
@@ -224,6 +225,15 @@
         return writeData(val);
     }
 
+    template <typename T, size_t N>
+    status_t writeFixedArray(const std::array<T, N>& val) {
+        return writeData(val);
+    }
+    template <typename T, size_t N>
+    status_t writeFixedArray(const std::optional<std::array<T, N>>& 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>
@@ -487,6 +497,15 @@
                             std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
 
+    template <typename T, size_t N>
+    status_t readFixedArray(std::array<T, N>* val) const {
+        return readData(val);
+    }
+    template <typename T, size_t N>
+    status_t readFixedArray(std::optional<std::array<T, N>>* val) const {
+        return readData(val);
+    }
+
     template<typename T>
     status_t            read(Flattenable<T>& val) const;
 
@@ -818,6 +837,16 @@
             || is_specialization_v<T, std::unique_ptr>
             || is_specialization_v<T, std::shared_ptr>;
 
+    // Tells if T is a fixed-size array.
+    template <typename T>
+    struct is_fixed_array : std::false_type {};
+
+    template <typename T, size_t N>
+    struct is_fixed_array<std::array<T, N>> : std::true_type {};
+
+    template <typename T>
+    static inline constexpr bool is_fixed_array_v = is_fixed_array<T>::value;
+
     // special int32 value to indicate NonNull or Null parcelables
     // This is fixed to be only 0 or 1 by contract, do not change.
     static constexpr int32_t kNonNullParcelableFlag = 1;
@@ -922,7 +951,9 @@
             if (!c) return writeData(static_cast<int32_t>(kNullVectorSize));
         } else if constexpr (std::is_base_of_v<Parcelable, T>) {
             if (!c) return writeData(static_cast<int32_t>(kNullParcelableFlag));
-        } else /* constexpr */ {  // could define this, but raise as error.
+        } else if constexpr (is_fixed_array_v<T>) {
+            if (!c) return writeData(static_cast<int32_t>(kNullVectorSize));
+        } else /* constexpr */ { // could define this, but raise as error.
             static_assert(dependent_false_v<CT>);
         }
         return writeData(*c);
@@ -961,6 +992,23 @@
         return OK;
     }
 
+    template <typename T, size_t N>
+    status_t writeData(const std::array<T, N>& val) {
+        static_assert(N <= std::numeric_limits<int32_t>::max());
+        status_t status = writeData(static_cast<int32_t>(N));
+        if (status != OK) return status;
+        if constexpr (is_pointer_equivalent_array_v<T>) {
+            static_assert(N <= std::numeric_limits<size_t>::max() / sizeof(T));
+            return write(val.data(), val.size() * sizeof(T));
+        } else /* constexpr */ {
+            for (const auto& t : val) {
+                status = writeData(t);
+                if (status != OK) return status;
+            }
+            return OK;
+        }
+    }
+
     // readData function overloads.
     // Implementation detail: Function overloading improves code readability over
     // template overloading, but prevents readData<T> from being used for those types.
@@ -1053,9 +1101,8 @@
         int32_t peek;
         status_t status = readData(&peek);
         if (status != OK) return status;
-        if constexpr (is_specialization_v<T, std::vector>
-                || std::is_same_v<T, String16>
-                || std::is_same_v<T, std::string>) {
+        if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T> ||
+                      std::is_same_v<T, String16> || std::is_same_v<T, std::string>) {
             if (peek == kNullVectorSize) {
                 c->reset();
                 return OK;
@@ -1065,12 +1112,15 @@
                 c->reset();
                 return OK;
             }
-        } else /* constexpr */ {  // could define this, but raise as error.
+        } else /* constexpr */ { // could define this, but raise as error.
             static_assert(dependent_false_v<CT>);
         }
         // create a new object.
         if constexpr (is_specialization_v<CT, std::optional>) {
-            c->emplace();
+            // Call default constructor explicitly
+            // - Clang bug: https://bugs.llvm.org/show_bug.cgi?id=35748
+            //   std::optional::emplace() doesn't work with nested types.
+            c->emplace(T());
         } else /* constexpr */ {
             T* const t = new (std::nothrow) T;  // contents read from Parcel below.
             if (t == nullptr) return NO_MEMORY;
@@ -1079,7 +1129,7 @@
         // rewind data ptr to reread (this is pretty quick), otherwise we could
         // pass an optional argument to readData to indicate a peeked value.
         setDataPosition(startPos);
-        if constexpr (is_specialization_v<T, std::vector>) {
+        if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T>) {
             return readData(&**c, READ_FLAG_SP_NULLABLE);  // nullable sp<> allowed now
         } else {
             return readData(&**c);
@@ -1142,6 +1192,41 @@
         return OK;
     }
 
+    template <typename T, size_t N>
+    status_t readData(std::array<T, N>* val, ReadFlags readFlags = READ_FLAG_NONE) const {
+        static_assert(N <= std::numeric_limits<int32_t>::max());
+        int32_t size;
+        status_t status = readInt32(&size);
+        if (status != OK) return status;
+        if (size < 0) return UNEXPECTED_NULL;
+        if (size != static_cast<int32_t>(N)) return BAD_VALUE;
+        if constexpr (is_pointer_equivalent_array_v<T>) {
+            auto data = reinterpret_cast<const T*>(readInplace(N * sizeof(T)));
+            if (data == nullptr) return BAD_VALUE;
+            memcpy(val->data(), data, N * sizeof(T));
+        } else if constexpr (is_specialization_v<T, sp>) {
+            for (auto& t : *val) {
+                if (readFlags & READ_FLAG_SP_NULLABLE) {
+                    status = readNullableStrongBinder(&t); // allow nullable
+                } else {
+                    status = readStrongBinder(&t);
+                }
+                if (status != OK) return status;
+            }
+        } else if constexpr (is_fixed_array_v<T>) { // pass readFlags down to nested arrays
+            for (auto& t : *val) {
+                status = readData(&t, readFlags);
+                if (status != OK) return status;
+            }
+        } else /* constexpr */ {
+            for (auto& t : *val) {
+                status = readData(&t);
+                if (status != OK) return status;
+            }
+        }
+        return OK;
+    }
+
     //-----------------------------------------------------------------------------
     private:
 
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 32406e5..077d915 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -233,6 +233,32 @@
     PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector),
     PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
 
+#define COMMA ,
+    PARCEL_READ_WITH_STATUS(std::array<uint8_t COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<uint8_t COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<char16_t COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<char16_t COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<std::string COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<std::string> COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<android::String16 COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<android::String16> COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<android::sp<android::IBinder> COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<android::sp<android::IBinder> COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<ExampleParcelable> COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<ByteEnum COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<ByteEnum COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<IntEnum COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<IntEnum COMMA 3>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<LongEnum COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<LongEnum COMMA 3>>, readFixedArray),
+    // nested arrays
+    PARCEL_READ_WITH_STATUS(std::array<std::array<uint8_t COMMA 3> COMMA 4>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<uint8_t COMMA 3> COMMA 4>>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray),
+    PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<std::optional<ExampleParcelable> COMMA 3> COMMA 4>>, readFixedArray),
+#undef COMMA
+
     [] (const android::Parcel& p, uint8_t /*len*/) {
         FUZZ_LOG() << "about to read flattenable";
         ExampleFlattenable f;