Special consideration for passing vec<interface> or vec<IFoo> to and from

methods. These remain unsupported inside structures.

Bug: 30570663
Test: hidl_test
Change-Id: Ic19db58d3c3ae8caa85a773a143fc148fd8affb6
diff --git a/CompoundType.cpp b/CompoundType.cpp
index f8ddc06..63514d8 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -35,7 +35,10 @@
     for (const auto &field : *fields) {
         const Type &type = field->type();
 
-        if (type.isBinder()) {
+        if (type.isBinder()
+                || (type.isVector()
+                    && static_cast<const VectorType *>(
+                        &type)->isVectorOfBinders())) {
             *errorMsg =
                 "Structs/Unions must not contain references to interfaces.";
 
diff --git a/VectorType.cpp b/VectorType.cpp
index d7eb581..461a05b 100644
--- a/VectorType.cpp
+++ b/VectorType.cpp
@@ -30,11 +30,20 @@
     mElementType->addNamedTypesToSet(set);
 }
 
+bool VectorType::isVector() const {
+    return true;
+}
+
+bool VectorType::isVectorOfBinders() const {
+    return mElementType->isBinder();
+}
+
 std::string VectorType::getCppType(StorageMode mode,
                                    std::string *extra,
                                    bool specifyNamespaces) const {
     std::string elementExtra;
-    const std::string elementBase = mElementType->getCppType(&elementExtra, specifyNamespaces);
+    const std::string elementBase =
+        mElementType->getCppType(&elementExtra, specifyNamespaces);
 
     const std::string base =
           std::string(specifyNamespaces ? "::android::hardware::" : "")
@@ -53,7 +62,13 @@
             return "const " + base + "&";
 
         case StorageMode_Result:
+        {
+            if (isVectorOfBinders()) {
+                return base;
+            }
+
             return "const " + base + "*";
+        }
     }
 }
 
@@ -84,6 +99,13 @@
         bool parcelObjIsPointer,
         bool isReader,
         ErrorMode mode) const {
+    if (isVectorOfBinders()) {
+        emitReaderWriterForVectorOfBinders(
+                out, name, parcelObj, parcelObjIsPointer, isReader, mode);
+
+        return;
+    }
+
     std::string baseExtra;
     std::string baseType = mElementType->getCppType(&baseExtra);
 
@@ -142,6 +164,92 @@
             "0 /* parentOffset */");
 }
 
+void VectorType::emitReaderWriterForVectorOfBinders(
+        Formatter &out,
+        const std::string &name,
+        const std::string &parcelObj,
+        bool parcelObjIsPointer,
+        bool isReader,
+        ErrorMode mode) const {
+    const std::string parcelObjDeref =
+        parcelObj + (parcelObjIsPointer ? "->" : ".");
+
+    if (isReader) {
+        out << "{\n";
+        out.indent();
+
+        const std::string sizeName = "_hidl_" + name + "_size";
+
+        out << "uint64_t "
+            << sizeName
+            << ";\n";
+
+        out << "_hidl_err = "
+            << parcelObjDeref
+            << "readUint64(&"
+            << sizeName
+            << ");\n";
+
+        handleError(out, mode);
+
+        out << name
+            << ".resize("
+            << sizeName
+            << ");\n\n"
+            << "for (size_t _hidl_index = 0; _hidl_index < "
+            << sizeName
+            << "; ++_hidl_index) {\n";
+
+        out.indent();
+
+        std::string extra;
+        out << mElementType->getCppType(&extra, true /* specifyNamespaces */)
+            << " _hidl_binder;\n";
+
+        mElementType->emitReaderWriter(
+                out,
+                "_hidl_binder",
+                parcelObj,
+                parcelObjIsPointer,
+                isReader,
+                mode);
+
+        out << name
+            << "[_hidl_index] = _hidl_binder;\n";
+
+        out.unindent();
+        out << "}\n";
+
+        out.unindent();
+        out << "}\n";
+    } else {
+        out << "_hidl_err = "
+            << parcelObjDeref
+            << "writeUint64("
+            << name
+            << ".size());\n";
+
+        handleError(out, mode);
+
+        out << "for (size_t _hidl_index = 0; _hidl_index < "
+            << name
+            << ".size(); ++_hidl_index) {\n";
+
+        out.indent();
+
+        mElementType->emitReaderWriter(
+                out,
+                name + "[_hidl_index]",
+                parcelObj,
+                parcelObjIsPointer,
+                isReader,
+                mode);
+
+        out.unindent();
+        out << "}\n";
+    }
+}
+
 void VectorType::emitReaderWriterEmbedded(
         Formatter &out,
         size_t depth,
@@ -580,7 +688,7 @@
 }
 
 bool VectorType::resultNeedsDeref() const {
-    return true;
+    return !isVectorOfBinders();
 }
 
 status_t VectorType::emitVtsTypeDeclarations(Formatter &out) const {
@@ -621,6 +729,10 @@
         return false;
     }
 
+    if (isVectorOfBinders()) {
+        return false;
+    }
+
     return true;
 }
 
diff --git a/VectorType.h b/VectorType.h
index e093777..333d69d 100644
--- a/VectorType.h
+++ b/VectorType.h
@@ -25,6 +25,9 @@
 struct VectorType : public TemplatedType {
     VectorType();
 
+    bool isVector() const override;
+    bool isVectorOfBinders() const;
+
     void addNamedTypesToSet(std::set<const FQName> &set) const override;
 
     std::string getCppType(
@@ -144,6 +147,14 @@
             const std::string &childName,
             const std::string &childOffsetText) const;
 
+    void emitReaderWriterForVectorOfBinders(
+            Formatter &out,
+            const std::string &name,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode) const;
+
     DISALLOW_COPY_AND_ASSIGN(VectorType);
 };
 
diff --git a/generateJava.cpp b/generateJava.cpp
index 2b14cf0..6ae84fe 100644
--- a/generateJava.cpp
+++ b/generateJava.cpp
@@ -108,8 +108,8 @@
                 "ERROR: This interface is not Java compatible. The Java backend"
                 " does NOT support union types nor native handles. "
                 "In addition, vectors of arrays are limited to at most "
-                "one-dimensional arrays and vectors of vectors are not "
-                "supported.\n");
+                "one-dimensional arrays and vectors of {vectors,interfaces} are"
+                " not supported.\n");
 
         return UNKNOWN_ERROR;
     }
diff --git a/hidl-gen_y.yy b/hidl-gen_y.yy
index 1b6ae5c..b79f195 100644
--- a/hidl-gen_y.yy
+++ b/hidl-gen_y.yy
@@ -673,7 +673,7 @@
       }
     | TEMPLATED '<' type '>'
       {
-          if ($3->isBinder()) {
+          if (!$1->isVector() && $3->isBinder()) {
               std::cerr << "ERROR: TemplatedType of interface types are not "
                         << "supported. at " << @3 << "\n";
 
diff --git a/test/main.cpp b/test/main.cpp
index 97ac84f..6063bef 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -59,11 +59,12 @@
 static std::string gServiceName;
 // end static storage
 
+using ::android::hardware::tests::foo::V1_0::Abc;
 using ::android::hardware::tests::foo::V1_0::IFoo;
 using ::android::hardware::tests::foo::V1_0::IFooCallback;
+using ::android::hardware::tests::foo::V1_0::ISimple;
 using ::android::hardware::tests::bar::V1_0::IBar;
 using ::android::hardware::tests::bar::V1_0::IHwBar;
-using ::android::hardware::tests::foo::V1_0::Abc;
 using ::android::hardware::tests::pointer::V1_0::IGraph;
 using ::android::hardware::tests::pointer::V1_0::IPointer;
 using ::android::hardware::IPCThreadState;
@@ -129,6 +130,19 @@
 // NOTE: duplicated code in Foo.cpp
 using std::to_string;
 
+struct Simple : public ISimple {
+    Simple(int32_t cookie)
+        : mCookie(cookie) {
+    }
+
+    Return<int32_t> getCookie() override {
+        return mCookie;
+    }
+
+private:
+    int32_t mCookie;
+};
+
 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);
@@ -768,6 +782,51 @@
                 }));
 }
 
+TEST_F(HidlTest, FooHaveAVectorOfInterfacesTest) {
+    hidl_vec<sp<ISimple> > in;
+    in.resize(16);
+    for (size_t i = 0; i < in.size(); ++i) {
+        in[i] = new Simple(i);
+    }
+
+    EXPECT_OK(foo->haveAVectorOfInterfaces(
+                in,
+                [&](const auto &out) {
+                    EXPECT_EQ(in.size(), out.size());
+                    for (size_t i = 0; i < in.size(); ++i) {
+                        int32_t inCookie = in[i]->getCookie();
+                        int32_t outCookie = out[i]->getCookie();
+                        EXPECT_EQ(inCookie, outCookie);
+                    }
+                }));
+}
+
+TEST_F(HidlTest, FooHaveAVectorOfGenericInterfacesTest) {
+    using ::android::hardware::tests::foo::V1_0::IHwSimple;
+    using ::android::hardware::tests::foo::V1_0::BnSimple;
+
+    hidl_vec<sp<android::hardware::IBinder> > in;
+    in.resize(16);
+    for (size_t i = 0; i < in.size(); ++i) {
+        sp<BnSimple> simpleStub = new BnSimple(new Simple(i));
+        in[i] = IHwSimple::asBinder(simpleStub);
+    }
+
+    EXPECT_OK(foo->haveAVectorOfGenericInterfaces(
+                in,
+                [&](const auto &out) {
+                    EXPECT_EQ(in.size(), out.size());
+                    for (size_t i = 0; i < in.size(); ++i) {
+                        sp<ISimple> inSimple = IHwSimple::asInterface(in[i]);
+                        sp<ISimple> outSimple = IHwSimple::asInterface(out[i]);
+
+                        int32_t inCookie = inSimple->getCookie();
+                        int32_t outCookie = outSimple->getCookie();
+                        EXPECT_EQ(inCookie, outCookie);
+                    }
+                }));
+}
+
 TEST_F(HidlTest, BarThisIsNewTest) {
     // Now the tricky part, get access to the derived interface.
     ALOGI("CLIENT call thisIsNew.");