Arrays in .hal files are now exposed to C++ as hidl_array<T, SIZE ...>

instead of as native arrays. This allows them to be copied which in turn lets
them exist in vectors. In the Java backend, vectors are limited to
one-dimensional arrays and scalar arrays are properly wrapped,
i.e. vec<uint8_t[]> => Vector<Byte[]>

Change-Id: I47524ec8423dfb41a436df36af8fa05eb8b3c0cc
Bug: 31682327
Test: hidl_test and hidl_test_java
diff --git a/ArrayType.cpp b/ArrayType.cpp
index 6b4a01b..f84540d 100644
--- a/ArrayType.cpp
+++ b/ArrayType.cpp
@@ -40,6 +40,10 @@
     mSizeComments.insert(mSizeComments.begin(), size->description());
 }
 
+size_t ArrayType::countDimensions() const {
+    return mSizes.size();
+}
+
 void ArrayType::addNamedTypesToSet(std::set<const FQName> &set) const {
     mElementType->addNamedTypesToSet(set);
 }
@@ -48,40 +52,41 @@
     return true;
 }
 
+Type *ArrayType::getElementType() const {
+    return mElementType;
+}
+
 std::string ArrayType::getCppType(StorageMode mode,
                                   std::string *extra,
                                   bool specifyNamespaces) const {
     const std::string base = mElementType->getCppType(extra, specifyNamespaces);
     CHECK(extra->empty());
 
-    size_t numArrayElements = 1;
-    for (auto size : mSizes) {
-        numArrayElements *= size;
+    std::string arrayType = "hidl_array<" + base;
+
+    for (size_t i = 0; i < mSizes.size(); ++i) {
+        arrayType += ", ";
+        arrayType += std::to_string(mSizes[i]);
+
+        arrayType += " /* ";
+        arrayType += mSizeComments[i];
+        arrayType += " */";
     }
 
-    std::string sizeComments = " /* " + base;
-    for (auto e : mSizeComments) {
-        sizeComments += "[" + e + "]";
-    }
-    sizeComments += " */";
+    arrayType += ">";
 
     switch (mode) {
         case StorageMode_Stack:
-        {
-            *extra += "[";
-            *extra += std::to_string(numArrayElements);
-            *extra += "]";
-            *extra += sizeComments;
-            return base;
-        }
+            return arrayType;
 
         case StorageMode_Argument:
+            return "const " + arrayType + "&";
+
         case StorageMode_Result:
-        {
-            *extra += sizeComments;
-            return "const " + base + "*";
-        }
+            return "const " + arrayType + "*";
     }
+
+    CHECK(!"Should not be here");
 }
 
 std::string ArrayType::getJavaType(
@@ -111,6 +116,10 @@
     return base;
 }
 
+std::string ArrayType::getJavaWrapperType() const {
+    return mElementType->getJavaWrapperType();
+}
+
 void ArrayType::emitReaderWriter(
         Formatter &out,
         const std::string &name,
@@ -130,10 +139,12 @@
         parcelObj + (parcelObjIsPointer ? "->" : ".");
 
     if (isReader) {
+        std::string extra;
+
         out << name
-            << " = (const "
-            << baseType
-            << " *)"
+            << " = ("
+            << getCppResultType(&extra)
+            << ")"
             << parcelObjDeref
             << "readBuffer(&"
             << parentName
@@ -149,17 +160,18 @@
         out.unindent();
         out << "}\n\n";
     } else {
+        size_t numArrayElements = 1;
+        for (auto size : mSizes) {
+            numArrayElements *= size;
+        }
+
         out << "_hidl_err = "
             << parcelObjDeref
             << "writeBuffer("
             << name
-            << ", ";
-
-        for (auto size : mSizes) {
-            out << size << " * ";
-        }
-
-        out << "sizeof("
+            << ".data(), "
+            << numArrayElements
+            << " * sizeof("
             << baseType
             << "), &"
             << parentName
@@ -224,7 +236,7 @@
     mElementType->emitReaderWriterEmbedded(
             out,
             depth + 1,
-            name + "[" + iteratorName + "]",
+            nameDeref + "data()[" + iteratorName + "]",
             false /* nameIsPointer */,
             parcelObj,
             parcelObjIsPointer,
@@ -245,6 +257,10 @@
     return mElementType->needsEmbeddedReadWrite();
 }
 
+bool ArrayType::resultNeedsDeref() const {
+    return true;
+}
+
 void ArrayType::emitJavaReaderWriter(
         Formatter &out,
         const std::string &parcelObj,
diff --git a/ArrayType.h b/ArrayType.h
index c0adbde..440a66c 100644
--- a/ArrayType.h
+++ b/ArrayType.h
@@ -36,7 +36,10 @@
 
     bool isArray() const override;
 
+    Type *getElementType() const;
+
     void addDimension(ConstantExpression *size);
+    size_t countDimensions() const;
 
     std::string getCppType(StorageMode mode,
                            std::string *extra,
@@ -47,6 +50,8 @@
     std::string getJavaType(
             std::string *extra, bool forInitializer) const override;
 
+    std::string getJavaWrapperType() const override;
+
     void emitReaderWriter(
             Formatter &out,
             const std::string &name,
@@ -68,6 +73,7 @@
             const std::string &offsetText) const override;
 
     bool needsEmbeddedReadWrite() const override;
+    bool resultNeedsDeref() const override;
 
     void emitJavaReaderWriter(
             Formatter &out,
diff --git a/VectorType.cpp b/VectorType.cpp
index 544315c..04a6ac3 100644
--- a/VectorType.cpp
+++ b/VectorType.cpp
@@ -16,6 +16,8 @@
 
 #include "VectorType.h"
 
+#include "ArrayType.h"
+
 #include <hidl-util/Formatter.h>
 #include <android-base/logging.h>
 
@@ -241,14 +243,19 @@
 
 void VectorType::emitJavaFieldInitializer(
         Formatter &out, const std::string &fieldName) const {
+    std::string extra;
+    mElementType->getJavaType(&extra);
+
     const std::string wrapperType = mElementType->getJavaWrapperType();
 
     out << "final Vector<"
         << wrapperType
+        << extra
         << "> "
         << fieldName
         << " = new Vector<"
         << wrapperType
+        << extra
         << ">();\n";
 }
 
@@ -334,8 +341,16 @@
                 iteratorName + " * " + std::to_string(elementSize),
                 true /* isReader */);
 
-        out << fieldName
-            << ".add(_hidl_vec_element);\n";
+        out << fieldName;
+
+        if (elementType->isArray()
+                && static_cast<const ArrayType *>(
+                    elementType)->getElementType()->resolveToScalarType()
+                        != nullptr) {
+            out << ".add(HwBlob.wrapArray(_hidl_vec_element));\n";
+        } else {
+            out << ".add(_hidl_vec_element);\n";
+        }
 
         out.unindent();
 
@@ -440,7 +455,15 @@
 }
 
 bool VectorType::isJavaCompatible() const {
-    return mElementType->isJavaCompatible();
+    if (!mElementType->isJavaCompatible()) {
+        return false;
+    }
+
+    if (mElementType->isArray()) {
+        return static_cast<ArrayType *>(mElementType)->countDimensions() == 1;
+    }
+
+    return true;
 }
 
 void VectorType::getAlignmentAndSize(size_t *align, size_t *size) const {
diff --git a/generateJava.cpp b/generateJava.cpp
index a5bdce6..b6d1d0d 100644
--- a/generateJava.cpp
+++ b/generateJava.cpp
@@ -107,7 +107,9 @@
     if (!isJavaCompatible()) {
         fprintf(stderr,
                 "ERROR: This interface is not Java compatible. The Java backend"
-                " does NOT support union types or native handles.\n");
+                " does NOT support union types nor native handles. "
+                "In addition, vectors of arrays are limited to at most "
+                "one-dimensional arrays.\n");
 
         return UNKNOWN_ERROR;
     }
diff --git a/test/java_test/hidl_test_java_native.cpp b/test/java_test/hidl_test_java_native.cpp
index f640f73..5c95948 100644
--- a/test/java_test/hidl_test_java_native.cpp
+++ b/test/java_test/hidl_test_java_native.cpp
@@ -17,6 +17,7 @@
 using ::android::hardware::tests::baz::V1_0::IBaz;
 using ::android::hardware::tests::baz::V1_0::IBazCallback;
 
+using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::hidl_string;
 using ::android::hardware::Return;
@@ -40,31 +41,37 @@
             const IBaz::Foo &foo, someOtherBaseMethod_cb _hidl_cb) override;
 
     Return<void> someMethodWithFooArrays(
-            const IBaz::Foo fooInput[2],
+            const hidl_array<IBaz::Foo, 2> &fooInput,
             someMethodWithFooArrays_cb _hidl_cb) override;
 
     Return<void> someMethodWithFooVectors(
             const hidl_vec<IBaz::Foo> &fooInput,
             someMethodWithFooVectors_cb _hidl_cb) override;
 
+    Return<void> someMethodWithVectorOfArray(
+            const IBase::VectorOfArray &in,
+            someMethodWithVectorOfArray_cb _hidl_cb) override;
+
     Return<void> transpose(
             const IBase::StringMatrix5x3 &in,
             transpose_cb _hidl_cb) override;
 
     Return<void> transpose2(
-            const hidl_string *in /* hidl_string[5][3] */,
+            const hidl_array<hidl_string, 5, 3> &in,
             transpose2_cb _hidl_cb) override;
 
     Return<bool> someBoolMethod(bool x) override;
 
     Return<void> someBoolArrayMethod(
-            const bool x[3], someBoolArrayMethod_cb _hidl_cb) override;
+            const hidl_array<bool, 3> &x,
+            someBoolArrayMethod_cb _hidl_cb) override;
 
     Return<void> someBoolVectorMethod(
             const hidl_vec<bool> &x, someBoolVectorMethod_cb _hidl_cb) override;
 
     Return<void> doSomethingElse(
-            const int32_t param[15], doSomethingElse_cb _hidl_cb) override;
+            const hidl_array<int32_t, 15> &param,
+            doSomethingElse_cb _hidl_cb) override;
 
     Return<void> doThis(float param) override;
 
@@ -86,7 +93,8 @@
     Return<IBaz::SomeEnum> useAnEnum(IBaz::SomeEnum zzz) override;
 
     Return<void> haveSomeStrings(
-            const hidl_string array[3], haveSomeStrings_cb _hidl_cb) override;
+            const hidl_array<hidl_string, 3> &array,
+            haveSomeStrings_cb _hidl_cb) override;
 
     Return<void> haveAStringVec(
             const hidl_vec<hidl_string>& vector,
@@ -110,11 +118,26 @@
 static std::string to_string(const IBase::StringMatrix5x3 &M);
 static std::string to_string(const IBase::StringMatrix3x5 &M);
 
-template<class T>
-static std::string array_to_string(const T *array, size_t size) {
+template<typename T>
+static std::string to_string(const hidl_vec<T> &vec) {
     std::string out;
     out = "[";
-    for (size_t i = 0; i < size; ++i) {
+    for (size_t i = 0; i < vec.size(); ++i) {
+        if (i > 0) {
+            out += ", ";
+        }
+        out += to_string(vec[i]);
+    }
+    out += "]";
+
+    return out;
+}
+
+template<typename T, size_t SIZE>
+static std::string to_string(const hidl_array<T, SIZE> &array) {
+    std::string out;
+    out = "[";
+    for (size_t i = 0; i < SIZE; ++i) {
         if (i > 0) {
             out += ", ";
         }
@@ -125,26 +148,30 @@
     return out;
 }
 
-template<class T>
-static std::string matrix_to_string(const T *array, size_t dim1, size_t dim2) {
+template<typename T, size_t SIZE1, size_t SIZE2>
+static std::string to_string(const hidl_array<T, SIZE1, SIZE2> &array) {
     std::string out;
     out = "[";
-    for (size_t i = 0; i < dim1; ++i) {
+    for (size_t i = 0; i < SIZE1; ++i) {
         if (i > 0) {
             out += ", ";
         }
-        out += array_to_string(&array[dim2 * i], dim2);
+
+        out += "[";
+        for (size_t j = 0; j < SIZE2; ++j) {
+            if (j > 0) {
+                out += ", ";
+            }
+
+            out += to_string(array[i][j]);
+        }
+        out += "]";
     }
     out += "]";
 
     return out;
 }
 
-template<class T>
-static std::string to_string(const hidl_vec<T> &vec) {
-    return array_to_string(&vec[0], vec.size());
-}
-
 static std::string to_string(bool x) {
     return x ? "true" : "false";
 }
@@ -175,31 +202,11 @@
 }
 
 static std::string to_string(const IBase::StringMatrix5x3 &M) {
-    std::string out;
-    out += "[";
-    for (int i = 0; i < 5; ++i) {
-        if (i > 0) {
-            out += ", ";
-        }
-        out += array_to_string(&M.s[3 * i], 3);
-    }
-    out += "]";
-
-    return out;
+    return to_string(M.s);
 }
 
 static std::string to_string(const IBase::StringMatrix3x5 &M) {
-    std::string out;
-    out += "[";
-    for (int i = 0; i < 3; ++i) {
-        if (i > 0) {
-            out += ", ";
-        }
-        out += array_to_string(&M.s[5 * i], 5);
-    }
-    out += "]";
-
-    return out;
+    return to_string(M.s);
 }
 
 Return<void> Baz::someOtherBaseMethod(
@@ -213,11 +220,12 @@
 }
 
 Return<void> Baz::someMethodWithFooArrays(
-        const IBaz::Foo fooInput[2], someMethodWithFooArrays_cb _hidl_cb) {
+        const hidl_array<IBaz::Foo, 2> &fooInput,
+        someMethodWithFooArrays_cb _hidl_cb) {
     LOG(INFO) << "Baz::someMethodWithFooArrays "
-              << array_to_string(fooInput, 2);
+              << to_string(fooInput);
 
-    IBaz::Foo fooOutput[2];
+    hidl_array<IBaz::Foo, 2> fooOutput;
     fooOutput[0] = fooInput[1];
     fooOutput[1] = fooInput[0];
 
@@ -242,15 +250,60 @@
     return Void();
 }
 
+static std::string VectorOfArray_to_string(const IBase::VectorOfArray &in) {
+    std::string out;
+    out += "VectorOfArray(";
+
+    for (size_t i = 0; i < in.addresses.size(); ++i) {
+        if (i > 0) {
+            out += ", ";
+        }
+
+        for (size_t j = 0; j < 6; ++j) {
+            if (j > 0) {
+                out += ":";
+            }
+
+            char tmp[3];
+            sprintf(tmp, "%02x", in.addresses[i][j]);
+
+            out += tmp;
+        }
+    }
+
+    out += ")";
+
+    return out;
+}
+
+Return<void> Baz::someMethodWithVectorOfArray(
+        const IBase::VectorOfArray &in,
+        someMethodWithVectorOfArray_cb _hidl_cb) {
+    LOG(INFO) << "Baz::someMethodWithVectorOfArray "
+              << VectorOfArray_to_string(in);
+
+    IBase::VectorOfArray out;
+
+    const size_t n = in.addresses.size();
+    out.addresses.resize(n);
+
+    for (size_t i = 0; i < n; ++i) {
+        out.addresses[i] = in.addresses[n - 1 - i];
+    }
+
+    _hidl_cb(out);
+
+    return Void();
+}
+
 Return<void> Baz::transpose(
         const IBase::StringMatrix5x3 &in, transpose_cb _hidl_cb) {
     LOG(INFO) << "Baz::transpose " << to_string(in);
 
     IBase::StringMatrix3x5 out;
-    size_t k = 0;
     for (size_t i = 0; i < 3; ++i) {
-        for (size_t j = 0; j < 5; ++j, ++k) {
-            out.s[k] = in.s[j * 3 + i];
+        for (size_t j = 0; j < 5; ++j) {
+            out.s[i][j] = in.s[j][i];
         }
     }
 
@@ -260,18 +313,17 @@
 }
 
 Return<void> Baz::transpose2(
-        const hidl_string *in /* hidl_string[5][3] */,
-        transpose2_cb _hidl_cb) {
-    LOG(INFO) << "Baz::transpose2 " << matrix_to_string(in, 5, 3);
+        const hidl_array<hidl_string, 5, 3> &in, transpose2_cb _hidl_cb) {
+    LOG(INFO) << "Baz::transpose2 " << to_string(in);
 
-    hidl_string out[3][5];
+    hidl_array<hidl_string, 3, 5> out;
     for (size_t i = 0; i < 3; ++i) {
         for (size_t j = 0; j < 5; ++j) {
-            out[i][j] = in[j * 3 + i];
+            out[i][j] = in[j][i];
         }
     }
 
-    _hidl_cb(&out[0][0]);
+    _hidl_cb(out);
 
     return Void();
 }
@@ -283,7 +335,7 @@
 }
 
 Return<void> Baz::someBoolArrayMethod(
-        const bool x[3], someBoolArrayMethod_cb _hidl_cb) {
+        const hidl_array<bool, 3> &x, someBoolArrayMethod_cb _hidl_cb) {
     LOG(INFO) << "Baz::someBoolArrayMethod("
         << to_string(x[0])
         << ", "
@@ -292,7 +344,7 @@
         << to_string(x[2])
         << ")";
 
-    bool out[4];
+    hidl_array<bool, 4> out;
     out[0] = !x[0];
     out[1] = !x[1];
     out[2] = !x[2];
@@ -319,10 +371,10 @@
 }
 
 Return<void> Baz::doSomethingElse(
-        const int32_t param[15], doSomethingElse_cb _hidl_cb) {
+        const hidl_array<int32_t, 15> &param, doSomethingElse_cb _hidl_cb) {
     LOG(INFO) << "Baz::doSomethingElse(...)";
 
-    int32_t result[32] = { 0 };
+    hidl_array<int32_t, 32> result;
     for (size_t i = 0; i < 15; ++i) {
         result[i] = 2 * param[i];
         result[15 + i] = param[i];
@@ -410,12 +462,12 @@
 }
 
 Return<void> Baz::haveSomeStrings(
-        const hidl_string array[3], haveSomeStrings_cb _hidl_cb) {
+        const hidl_array<hidl_string, 3> &array, haveSomeStrings_cb _hidl_cb) {
     LOG(INFO) << "haveSomeStrings("
-              << array_to_string(array, 3)
+              << to_string(array)
               << ")";
 
-    hidl_string result[2];
+    hidl_array<hidl_string, 2> result;
     result[0] = "Hello";
     result[1] = "World";
 
@@ -514,7 +566,7 @@
 }
 
 TEST_F(HidlTest, BazSomeMethodWithFooArraysTest) {
-    IBase::Foo foo[2];
+    hidl_array<IBase::Foo, 2> foo;
 
     foo[0].x = 1;
     foo[0].y.z = 2.5;
@@ -541,7 +593,7 @@
                 foo,
                 [&](const auto &result) {
                     EXPECT_EQ(
-                        array_to_string(result, 2),
+                        to_string(result),
                         "[Foo(x = 2, "
                              "y = Bar(z = -2.500000, s = 'Morituri te salutant'), "
                              "aaa = [Bar(z = 2.000000, s = 'Alea iacta est: 0'), "
@@ -602,6 +654,30 @@
                 }));
 }
 
+TEST_F(HidlTest, BazSomeMethodWithVectorOfArray) {
+    IBase::VectorOfArray in;
+    in.addresses.resize(3);
+
+    size_t k = 0;
+    for (size_t i = 0; i < in.addresses.size(); ++i) {
+        for (size_t j = 0; j < 6; ++j, ++k) {
+            in.addresses[i][j] = k;
+        }
+    }
+
+    EXPECT_OK(
+            baz->someMethodWithVectorOfArray(
+                in,
+                [&](const auto &out) {
+                    EXPECT_EQ(
+                        VectorOfArray_to_string(out),
+                        "VectorOfArray("
+                          "0c:0d:0e:0f:10:11, "
+                          "06:07:08:09:0a:0b, "
+                          "00:01:02:03:04:05)");
+                }));
+}
+
 static std::string numberToEnglish(int x) {
     static const char *const kDigits[] = {
         "zero",
@@ -651,10 +727,9 @@
 TEST_F(HidlTest, BazTransposeTest) {
     IBase::StringMatrix5x3 in;
 
-    int k = 0;
     for (int i = 0; i < 5; ++i) {
-        for (int j = 0; j < 3; ++j, ++k) {
-            in.s[k] = numberToEnglish(3 * i + j + 1).c_str();
+        for (int j = 0; j < 3; ++j) {
+            in.s[i][j] = numberToEnglish(3 * i + j + 1).c_str();
         }
     }
 
@@ -670,12 +745,11 @@
 }
 
 TEST_F(HidlTest, BazTranspose2Test) {
-    hidl_string in[5 * 3];
+    hidl_array<hidl_string, 5, 3> in;
 
-    int k = 0;
     for (int i = 0; i < 5; ++i) {
-        for (int j = 0; j < 3; ++j, ++k) {
-            in[k] = numberToEnglish(3 * i + j + 1).c_str();
+        for (int j = 0; j < 3; ++j) {
+            in[i][j] = numberToEnglish(3 * i + j + 1).c_str();
         }
     }
 
@@ -683,7 +757,7 @@
                 in,
                 [&](const auto &out) {
                     EXPECT_EQ(
-                        matrix_to_string(out, 3, 5),
+                        to_string(out),
                         "[['one', 'four', 'seven', 'ten', 'thirteen'], "
                          "['two', 'five', 'eight', 'eleven', 'fourteen'], "
                          "['three', 'six', 'nine', 'twelve', 'fifteen']]");
@@ -697,14 +771,17 @@
 }
 
 TEST_F(HidlTest, BazSomeBoolArrayMethodTest) {
-    const bool someBoolArray[3] = { true, false, true };
+    hidl_array<bool, 3> someBoolArray;
+    someBoolArray[0] = true;
+    someBoolArray[1] = false;
+    someBoolArray[2] = true;
 
     EXPECT_OK(
             baz->someBoolArrayMethod(
                 someBoolArray,
                 [&](const auto &result) {
                     EXPECT_EQ(
-                        array_to_string(result, 4),
+                        to_string(result),
                         "[false, true, false, true]");
                 }));
 }
@@ -743,7 +820,7 @@
 }
 
 TEST_F(HidlTest, BazDoSomethingElseMethodTest) {
-    int32_t param[15];
+    hidl_array<int32_t, 15> param;
     for (size_t i = 0; i < 15; ++i) {
         param[i] = i;
     }
@@ -753,7 +830,7 @@
                 param,
                 [&](const auto &result) {
                     EXPECT_EQ(
-                        array_to_string(result, 32),
+                        to_string(result),
                         "[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, "
                         "28, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, "
                         "1, 2]");
@@ -780,7 +857,7 @@
                 vec_param,
                 [&](const auto &result) {
                     EXPECT_EQ(
-                        array_to_string(&result[0], result.size()),
+                        to_string(result),
                         "[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, "
                         "28]");
                 }));
@@ -798,7 +875,7 @@
 }
 
 TEST_F(HidlTest, BazHaveSomeStringsMethodTest) {
-    hidl_string string_params[3];
+    hidl_array<hidl_string, 3> string_params;
     string_params[0] = "one";
     string_params[1] = "two";
     string_params[2] = "three";
@@ -807,8 +884,7 @@
             baz->haveSomeStrings(
                 string_params,
                 [&](const auto &result) {
-                    EXPECT_EQ(
-                        array_to_string(result, 2), "['Hello', 'World']");
+                    EXPECT_EQ(to_string(result), "['Hello', 'World']");
                 }));
 }
 
@@ -824,9 +900,7 @@
             baz->haveAStringVec(
                 string_vec,
                 [&](const auto &result) {
-                    EXPECT_EQ(
-                        array_to_string(&result[0], result.size()),
-                        "['Hello', 'World']");
+                    EXPECT_EQ(to_string(result), "['Hello', 'World']");
                 }));
 }
 
diff --git a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
index 0882043..0a09447 100644
--- a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
+++ b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
@@ -202,6 +202,34 @@
         return toString(M.s);
     }
 
+    public static String toString(IBase.VectorOfArray vec) {
+        StringBuilder out = new StringBuilder();
+
+        out.append("VectorOfArray(");
+        for (int i = 0; i < vec.addresses.size(); ++i) {
+            if (i > 0) {
+                out.append(", ");
+            }
+
+            Byte[] address = vec.addresses.elementAt(i);
+
+            for (int j = 0; j < 6; ++j) {
+                if (j > 0) {
+                    out.append(":");
+                }
+
+                byte b = address[j];
+                if (b < 16) {
+                    out.append("0");
+                }
+                out.append(Integer.toHexString(b));
+            }
+        }
+
+        out.append(")");
+        return out.toString();
+    }
+
     private void ExpectTrue(boolean x) {
         if (x) {
             return;
@@ -421,6 +449,28 @@
         }
 
         {
+            IBase.VectorOfArray in = new IBase.VectorOfArray();
+
+            int k = 0;
+            for (int i = 0; i < 3; ++i) {
+                Byte[] mac = new Byte[6];
+                for (int j = 0; j < 6; ++j, ++k) {
+                    mac[j] = (byte)k;
+                }
+
+                in.addresses.add(mac);
+            }
+
+            IBase.VectorOfArray out = proxy.someMethodWithVectorOfArray(in);
+
+            Expect(toString(out),
+                   "VectorOfArray("
+                     + "0c:0d:0e:0f:10:11, "
+                     + "06:07:08:09:0a:0b, "
+                     + "00:01:02:03:04:05)");
+        }
+
+        {
             IBase.StringMatrix5x3 in = new IBase.StringMatrix5x3();
             for (int i = 0; i < 5; ++i) {
                 for (int j = 0; j < 3; ++j) {
@@ -554,6 +604,19 @@
             return fooOutput;
         }
 
+        public IBase.VectorOfArray someMethodWithVectorOfArray(
+                IBase.VectorOfArray in) {
+            Log.d(TAG, "Baz someMethodWithVectorOfArray " + HidlTestJava.toString(in));
+
+            IBase.VectorOfArray out = new IBase.VectorOfArray();
+            int n = in.addresses.size();
+            for (int i = 0; i < n; ++i) {
+                out.addresses.add(in.addresses.elementAt(n - i - 1));
+            }
+
+            return out;
+        }
+
         public IBase.StringMatrix3x5 transpose(IBase.StringMatrix5x3 in) {
             Log.d(TAG, "Baz transpose " + HidlTestJava.toString(in));
 
diff --git a/test/main.cpp b/test/main.cpp
index f092c85..ed4a044 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -34,6 +34,7 @@
 using ::android::hardware::tests::foo::V1_0::Abc;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
+using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::hidl_string;
 using ::android::sp;
@@ -41,19 +42,19 @@
 using ::android::Condition;
 
 struct FooCallback : public IFooCallback {
-    FooCallback() : invokeInfo{}, mLock{}, mCond{} {}
+    FooCallback() : mLock{}, mCond{} {}
     Return<void> heyItsYou(const sp<IFooCallback> &cb) override;
     Return<bool> heyItsYouIsntIt(const sp<IFooCallback> &cb) override;
     Return<void> heyItsTheMeaningOfLife(uint8_t tmol) override;
     Return<void> reportResults(int64_t ns, reportResults_cb cb) override;
-    Return<void> youBlockedMeFor(const int64_t ns[3]) override;
+    Return<void> youBlockedMeFor(const hidl_array<int64_t, 3> &ns) override;
 
     static constexpr nsecs_t DELAY_S = 1;
     static constexpr nsecs_t DELAY_NS = seconds_to_nanoseconds(DELAY_S);
     static constexpr nsecs_t TOLERANCE_NS = milliseconds_to_nanoseconds(10);
     static constexpr nsecs_t ONEWAY_TOLERANCE_NS = milliseconds_to_nanoseconds(1);
 
-    InvokeInfo invokeInfo[3];
+    hidl_array<InvokeInfo, 3> invokeInfo;
     Mutex mLock;
     Condition mCond;
 };
@@ -116,7 +117,7 @@
     return Void();
 }
 
-Return<void> FooCallback::youBlockedMeFor(const int64_t ns[3]) {
+Return<void> FooCallback::youBlockedMeFor(const hidl_array<int64_t, 3> &ns) {
     for (size_t i = 0; i < 3; i++) {
         invokeInfo[i].callerBlockedNs = ns[i];
     }
@@ -135,7 +136,8 @@
             double d) override;
 
     Return<void> doSomethingElse(
-            const int32_t param[15], doSomethingElse_cb _cb) override;
+            const hidl_array<int32_t, 15> &param,
+            doSomethingElse_cb _cb) override;
 
     Return<void> doStuffAndReturnAString(
             doStuffAndReturnAString_cb _cb) override;
@@ -150,12 +152,12 @@
 
     Return<void> haveAGooberVec(const hidl_vec<Goober>& param) override;
     Return<void> haveAGoober(const Goober &g) override;
-    Return<void> haveAGooberArray(const Goober lots[20]) override;
+    Return<void> haveAGooberArray(const hidl_array<Goober, 20> &lots) override;
 
     Return<void> haveATypeFromAnotherFile(const Abc &def) override;
 
     Return<void> haveSomeStrings(
-            const hidl_string array[3],
+            const hidl_array<hidl_string, 3> &array,
             haveSomeStrings_cb _cb) override;
 
     Return<void> haveAStringVec(
@@ -163,7 +165,7 @@
             haveAStringVec_cb _cb) override;
 
     Return<void> transposeMe(
-            const float *in /* float[3][5] */, transposeMe_cb _cb) override;
+            const hidl_array<float, 3, 5> &in, transposeMe_cb _cb) override;
 
     Return<void> callingDrWho(
             const MultiDimensional &in,
@@ -173,7 +175,7 @@
             const StringMatrix5x3 &in, transpose_cb _hidl_cb) override;
 
     Return<void> transpose2(
-            const hidl_string *in /* hidl_string[5][3] */,
+            const hidl_array<hidl_string, 5, 3> &in,
             transpose2_cb _hidl_cb) override;
 
     Return<void> thisIsNew() override;
@@ -203,10 +205,10 @@
 }
 
 Return<void> Bar::doSomethingElse(
-        const int32_t param[15], doSomethingElse_cb _cb) {
+        const hidl_array<int32_t, 15> &param, doSomethingElse_cb _cb) {
     ALOGI("SERVER(Bar) doSomethingElse(...)");
 
-    int32_t result[32] = { 0 };
+    hidl_array<int32_t, 32> result;
     for (size_t i = 0; i < 15; ++i) {
         result[i] = 2 * param[i];
         result[15 + i] = param[i];
@@ -253,7 +255,7 @@
 
     if (cb != NULL) {
 
-        nsecs_t c[3];
+        hidl_array<nsecs_t, 3> c;
         ALOGI("SERVER(Bar) callMe %p calling IFooCallback::heyItsYou, " \
               "should return immediately", cb.get());
         c[0] = systemTime();
@@ -309,8 +311,8 @@
     return Void();
 }
 
-Return<void> Bar::haveAGooberArray(const Goober lots[20]) {
-    ALOGI("SERVER(Bar) haveAGooberArray lots = %p", lots);
+Return<void> Bar::haveAGooberArray(const hidl_array<Goober, 20> & /* lots */) {
+    ALOGI("SERVER(Bar) haveAGooberArray");
 
     return Void();
 }
@@ -322,14 +324,14 @@
 }
 
 Return<void> Bar::haveSomeStrings(
-        const hidl_string array[3],
+        const hidl_array<hidl_string, 3> &array,
         haveSomeStrings_cb _cb) {
     ALOGI("SERVER(Bar) haveSomeStrings([\"%s\", \"%s\", \"%s\"])",
           array[0].c_str(),
           array[1].c_str(),
           array[2].c_str());
 
-    hidl_string result[2];
+    hidl_array<hidl_string, 2> result;
     result[0] = "Hello";
     result[1] = "World";
 
@@ -357,41 +359,62 @@
     return Void();
 }
 
-static std::string FloatArray2DToString(const float *x, size_t n1, size_t n2) {
-    std::string s;
-    s += "[";
-    for (size_t i = 0; i < n1; ++i) {
+using std::to_string;
+
+static std::string to_string(const IFoo::StringMatrix5x3 &M);
+static std::string to_string(const IFoo::StringMatrix3x5 &M);
+static std::string to_string(const hidl_string &s);
+
+template<typename T, size_t SIZE1, size_t SIZE2>
+static std::string to_string(const hidl_array<T, SIZE1, SIZE2> &array) {
+    std::string out;
+    out = "[";
+    for (size_t i = 0; i < SIZE1; ++i) {
         if (i > 0) {
-            s += ", ";
+            out += ", ";
         }
 
-        s += "[";
-        for (size_t j = 0; j < n2; ++j) {
+        out += "[";
+        for (size_t j = 0; j < SIZE2; ++j) {
             if (j > 0) {
-                s += ", ";
+                out += ", ";
             }
-            s += std::to_string(x[i * n2 + j]);
-        }
-        s += "]";
-    }
-    s += "]";
 
-    return s;
+            out += to_string(array[i][j]);
+        }
+        out += "]";
+    }
+    out += "]";
+
+    return out;
+}
+
+static std::string to_string(const IFoo::StringMatrix5x3 &M) {
+    return to_string(M.s);
+}
+
+static std::string to_string(const IFoo::StringMatrix3x5 &M) {
+    return to_string(M.s);
+}
+
+static std::string to_string(const hidl_string &s) {
+    return std::string("'") + s.c_str() + "'";
 }
 
 Return<void> Bar::transposeMe(
-        const float *in /* float[3][5] */, transposeMe_cb _cb) {
-    ALOGI("SERVER(Bar) transposeMe(%s)",
-          FloatArray2DToString(in, 3, 5).c_str());
+        const hidl_array<float, 3, 5> &in, transposeMe_cb _cb) {
+    ALOGI("SERVER(Bar) transposeMe(%s)", to_string(in).c_str());
 
-    float out[5][3];
+    hidl_array<float, 5, 3> out;
     for (size_t i = 0; i < 5; ++i) {
         for (size_t j = 0; j < 3; ++j) {
-            out[i][j] = in[5 * j + i];
+            out[i][j] = in[j][i];
         }
     }
 
-    _cb(&out[0][0]);
+    ALOGI("SERVER(Bar) transposeMe returning %s", to_string(out).c_str());
+
+    _cb(out);
 
     return Void();
 }
@@ -427,7 +450,7 @@
                 s += ", ";
             }
 
-            s += QuuxToString(val.quuxMatrix[k]);
+            s += QuuxToString(val.quuxMatrix[i][j]);
         }
     }
     s += "]";
@@ -442,13 +465,10 @@
     ALOGI("SERVER(Bar) callingDrWho(%s)", MultiDimensionalToString(in).c_str());
 
     MultiDimensional out;
-    size_t k = 0;
     for (size_t i = 0; i < 5; ++i) {
-        for (size_t j = 0; j < 3; ++j, ++k) {
-            size_t k_prime = (4 - i) * 3 + (2 - j);
-
-            out.quuxMatrix[k].first = in.quuxMatrix[k_prime].last;
-            out.quuxMatrix[k].last = in.quuxMatrix[k_prime].first;
+        for (size_t j = 0; j < 3; ++j) {
+            out.quuxMatrix[i][j].first = in.quuxMatrix[4 - i][2 - j].last;
+            out.quuxMatrix[i][j].last = in.quuxMatrix[4 - i][2 - j].first;
         }
     }
 
@@ -457,62 +477,13 @@
     return Void();
 }
 
-using std::to_string;
-
-static std::string to_string(const IFoo::StringMatrix5x3 &M);
-static std::string to_string(const IFoo::StringMatrix3x5 &M);
-static std::string to_string(const hidl_string &s);
-
-template<class T>
-static std::string array_to_string(const T *array, size_t size) {
-    std::string out;
-    out = "[";
-    for (size_t i = 0; i < size; ++i) {
-        if (i > 0) {
-            out += ", ";
-        }
-        out += to_string(array[i]);
-    }
-    out += "]";
-
-    return out;
-}
-
-template<class T>
-static std::string matrix_to_string(const T *array, size_t dim1, size_t dim2) {
-    std::string out;
-    out = "[";
-    for (size_t i = 0; i < dim1; ++i) {
-        if (i > 0) {
-            out += ", ";
-        }
-        out += array_to_string(&array[dim2 * i], dim2);
-    }
-    out += "]";
-
-    return out;
-}
-
-static std::string to_string(const IFoo::StringMatrix5x3 &M) {
-    return matrix_to_string(M.s, 5, 3);
-}
-
-static std::string to_string(const IFoo::StringMatrix3x5 &M) {
-    return matrix_to_string(M.s, 3, 5);
-}
-
-static std::string to_string(const hidl_string &s) {
-    return std::string("'") + s.c_str() + "'";
-}
-
 Return<void> Bar::transpose(const StringMatrix5x3 &in, transpose_cb _hidl_cb) {
     LOG(INFO) << "SERVER(Bar) transpose " << to_string(in);
 
     StringMatrix3x5 out;
-    size_t k = 0;
     for (size_t i = 0; i < 3; ++i) {
-        for (size_t j = 0; j < 5; ++j, ++k) {
-            out.s[k] = in.s[j * 3 + i];
+        for (size_t j = 0; j < 5; ++j) {
+            out.s[i][j] = in.s[j][i];
         }
     }
 
@@ -522,17 +493,17 @@
 }
 
 Return<void> Bar::transpose2(
-        const hidl_string *in /* hidl_string[5][3] */, transpose2_cb _hidl_cb) {
-    LOG(INFO) << "SERVER(Bar) transpose2 " << matrix_to_string(in, 5, 3);
+        const hidl_array<hidl_string, 5, 3> &in, transpose2_cb _hidl_cb) {
+    LOG(INFO) << "SERVER(Bar) transpose2 " << to_string(in);
 
-    hidl_string out[3][5];
+    hidl_array<hidl_string, 3, 5> out;
     for (size_t i = 0; i < 3; ++i) {
         for (size_t j = 0; j < 5; ++j) {
-            out[i][j] = in[j * 3 + i];
+            out[i][j] = in[j][i];
         }
     }
 
-    _hidl_cb(&out[0][0]);
+    _hidl_cb(out);
 
     return Void();
 }
@@ -687,7 +658,7 @@
 TEST_F(HidlTest, FooDoSomethingElseTest) {
 
     ALOGI("CLIENT call doSomethingElse");
-    int32_t param[15];
+    hidl_array<int32_t, 15> param;
     for (size_t i = 0; i < sizeof(param) / sizeof(param[0]); ++i) {
         param[i] = i;
     }
@@ -759,7 +730,7 @@
 
     fooCb->reportResults(reportResultsNs,
                 [&](int64_t timeLeftNs,
-                    const IFooCallback::InvokeInfo invokeResults[3]) {
+                    const hidl_array<IFooCallback::InvokeInfo, 3> &invokeResults) {
         ALOGI("CLIENT: FooCallback::reportResults() is returning data.");
         ALOGI("CLIENT: Waited for %" PRId64 " milliseconds.",
               nanoseconds_to_milliseconds(reportResultsNs - timeLeftNs));
@@ -806,7 +777,7 @@
     ALOGI("CLIENT haveaGoober returned.");
 
     ALOGI("CLIENT call haveAGooberArray.");
-    IFoo::Goober gooberArrayParam[20];
+    hidl_array<IFoo::Goober, 20> gooberArrayParam;
     EXPECT_OK(foo->haveAGooberArray(gooberArrayParam));
     ALOGI("CLIENT haveAGooberArray returned.");
 }
@@ -825,7 +796,7 @@
 
 TEST_F(HidlTest, FooHaveSomeStringsTest) {
     ALOGI("CLIENT call haveSomeStrings.");
-    hidl_string stringArrayParam[3];
+    hidl_array<hidl_string, 3> stringArrayParam;
     stringArrayParam[0] = "What";
     stringArrayParam[1] = "a";
     stringArrayParam[2] = "disaster";
@@ -845,7 +816,7 @@
 }
 
 TEST_F(HidlTest, FooTransposeMeTest) {
-    float in[3][5];
+    hidl_array<float, 3, 5> in;
     float k = 1.0f;
     for (size_t i = 0; i < 3; ++i) {
         for (size_t j = 0; j < 5; ++j, ++k) {
@@ -853,18 +824,17 @@
         }
     }
 
-    ALOGI("CLIENT call transposeMe(%s).",
-          FloatArray2DToString(&in[0][0], 3, 5).c_str());
+    ALOGI("CLIENT call transposeMe(%s).", to_string(in).c_str());
 
     EXPECT_OK(foo->transposeMe(
-                &in[0][0],
+                in,
                 [&](const auto &out) {
                     ALOGI("CLIENT transposeMe returned %s.",
-                          FloatArray2DToString(out, 5, 3).c_str());
+                          to_string(out).c_str());
 
                     for (size_t i = 0; i < 3; ++i) {
                         for (size_t j = 0; j < 5; ++j) {
-                            EXPECT_EQ(out[3 * j + i], in[i][j]);
+                            EXPECT_EQ(out[j][i], in[i][j]);
                         }
                     }
                 }));
@@ -876,8 +846,8 @@
     size_t k = 0;
     for (size_t i = 0; i < 5; ++i) {
         for (size_t j = 0; j < 3; ++j, ++k) {
-            in.quuxMatrix[k].first = ("First " + std::to_string(k)).c_str();
-            in.quuxMatrix[k].last = ("Last " + std::to_string(15-k)).c_str();
+            in.quuxMatrix[i][j].first = ("First " + std::to_string(k)).c_str();
+            in.quuxMatrix[i][j].last = ("Last " + std::to_string(15-k)).c_str();
         }
     }
 
@@ -893,15 +863,13 @@
                     size_t k = 0;
                     for (size_t i = 0; i < 5; ++i) {
                         for (size_t j = 0; j < 3; ++j, ++k) {
-                            size_t k_prime = (4 - i) * 3 + (2 - j);
+                            EXPECT_STREQ(
+                                out.quuxMatrix[i][j].first.c_str(),
+                                in.quuxMatrix[4 - i][2 - j].last.c_str());
 
                             EXPECT_STREQ(
-                                out.quuxMatrix[k].first.c_str(),
-                                in.quuxMatrix[k_prime].last.c_str());
-
-                            EXPECT_STREQ(
-                                out.quuxMatrix[k].last.c_str(),
-                                in.quuxMatrix[k_prime].first.c_str());
+                                out.quuxMatrix[i][j].last.c_str(),
+                                in.quuxMatrix[4 - i][2 - j].first.c_str());
                         }
                     }
                 }));
@@ -956,10 +924,9 @@
 TEST_F(HidlTest, FooTransposeTest) {
     IFoo::StringMatrix5x3 in;
 
-    int k = 0;
     for (int i = 0; i < 5; ++i) {
-        for (int j = 0; j < 3; ++j, ++k) {
-            in.s[k] = numberToEnglish(3 * i + j + 1).c_str();
+        for (int j = 0; j < 3; ++j) {
+            in.s[i][j] = numberToEnglish(3 * i + j + 1).c_str();
         }
     }
 
@@ -975,12 +942,11 @@
 }
 
 TEST_F(HidlTest, FooTranspose2Test) {
-    hidl_string in[5 * 3];
+    hidl_array<hidl_string, 5, 3> in;
 
-    int k = 0;
     for (int i = 0; i < 5; ++i) {
-        for (int j = 0; j < 3; ++j, ++k) {
-            in[k] = numberToEnglish(3 * i + j + 1).c_str();
+        for (int j = 0; j < 3; ++j) {
+            in[i][j] = numberToEnglish(3 * i + j + 1).c_str();
         }
     }
 
@@ -988,7 +954,7 @@
                 in,
                 [&](const auto &out) {
                     EXPECT_EQ(
-                        matrix_to_string(out, 3, 5),
+                        to_string(out),
                         "[['one', 'four', 'seven', 'ten', 'thirteen'], "
                          "['two', 'five', 'eight', 'eleven', 'fourteen'], "
                          "['three', 'six', 'nine', 'twelve', 'fifteen']]");