Pointer support and embedded types in HIDL.

* Pointers work per transaction. Don't work
  across transactions.
* ref<T> in HIDL translates to T const* in C++.
* No Java support.
* Embedded types like ref<vec<vec<int32_t>>>
  or vec<ref<T>> is supported. Pointers to
  pointers like ref<ref<ref<T>>> is supported.
* Array of pointers and pointer to array supported.
* Pointer inside a union is NOT supported.

Test: `mma`
Test: `make hidl_test && adb sync && adb shell hidl_test`
      Note that this only works with a kernel patch.

Bug: 31300815
Bug: 31349114

Change-Id: I15b74ca74a801009cc8bdc7132bd53d0185dbcbf
diff --git a/AST.h b/AST.h
index 4f1d373..46d3c0b 100644
--- a/AST.h
+++ b/AST.h
@@ -213,6 +213,15 @@
             Type::ErrorMode mode,
             bool addPrefixToName) const;
 
+    void emitCppResolveReferences(
+            Formatter &out,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            const TypedVar *arg,
+            bool isReader,
+            Type::ErrorMode mode,
+            bool addPrefixToName) const;
+
     void emitJavaReaderWriter(
             Formatter &out,
             const std::string &parcelObj,
diff --git a/Android.bp b/Android.bp
index 07fd62c..eb0bcfb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -40,6 +40,7 @@
         "Method.cpp",
         "NamedType.cpp",
         "PredefinedType.cpp",
+        "RefType.cpp",
         "ScalarType.cpp",
         "Scope.cpp",
         "StringType.cpp",
diff --git a/ArrayType.cpp b/ArrayType.cpp
index f84540d..3fb2691 100644
--- a/ArrayType.cpp
+++ b/ArrayType.cpp
@@ -216,17 +216,12 @@
 
     std::string iteratorName = "_hidl_index_" + std::to_string(depth);
 
-    size_t numArrayElements = 1;
-    for (auto size : mSizes) {
-        numArrayElements *= size;
-    }
-
     out << "for (size_t "
         << iteratorName
         << " = 0; "
         << iteratorName
         << " < "
-        << numArrayElements
+        << dimension()
         << "; ++"
         << iteratorName
         << ") {\n";
@@ -253,10 +248,90 @@
     out << "}\n\n";
 }
 
+void ArrayType::emitResolveReferences(
+            Formatter &out,
+            const std::string &name,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode) const {
+    emitResolveReferencesEmbedded(
+        out,
+        0 /* depth */,
+        name,
+        name /* sanitizedName */,
+        nameIsPointer,
+        parcelObj,
+        parcelObjIsPointer,
+        isReader,
+        mode,
+        "_hidl_" + name + "_parent",
+        "0 /* parentOffset */");
+}
+
+void ArrayType::emitResolveReferencesEmbedded(
+            Formatter &out,
+            size_t depth,
+            const std::string &name,
+            const std::string &sanitizedName,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode,
+            const std::string &parentName,
+            const std::string &offsetText) const {
+    CHECK(needsResolveReferences() && mElementType->needsResolveReferences());
+
+    const std::string nameDeref = name + (nameIsPointer ? "->" : ".");
+
+    std::string baseExtra;
+    std::string baseType = mElementType->getCppType(&baseExtra);
+
+    std::string iteratorName = "_hidl_index_" + std::to_string(depth);
+
+    out << "for (size_t "
+        << iteratorName
+        << " = 0; "
+        << iteratorName
+        << " < "
+        << dimension()
+        << "; ++"
+        << iteratorName
+        << ") {\n";
+
+    out.indent();
+
+    mElementType->emitResolveReferencesEmbedded(
+        out,
+        depth + 1,
+        nameDeref + "data()[" + iteratorName + "]",
+        sanitizedName + "_indexed",
+        false /* nameIsPointer */,
+        parcelObj,
+        parcelObjIsPointer,
+        isReader,
+        mode,
+        parentName,
+        offsetText + " + " + iteratorName + " * sizeof("
+        + baseType
+        + ")");
+
+    out.unindent();
+
+    out << "}\n\n";
+}
+
+
 bool ArrayType::needsEmbeddedReadWrite() const {
     return mElementType->needsEmbeddedReadWrite();
 }
 
+bool ArrayType::needsResolveReferences() const {
+    return mElementType->needsResolveReferences();
+}
+
 bool ArrayType::resultNeedsDeref() const {
     return true;
 }
@@ -428,5 +503,13 @@
     }
 }
 
+size_t ArrayType::dimension() const {
+    size_t numArrayElements = 1;
+    for (auto size : mSizes) {
+        numArrayElements *= size;
+    }
+    return numArrayElements;
+}
+
 }  // namespace android
 
diff --git a/ArrayType.h b/ArrayType.h
index 440a66c..4514c2d 100644
--- a/ArrayType.h
+++ b/ArrayType.h
@@ -72,7 +72,30 @@
             const std::string &parentName,
             const std::string &offsetText) const override;
 
+    void emitResolveReferences(
+            Formatter &out,
+            const std::string &name,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode) const override;
+
+    void emitResolveReferencesEmbedded(
+            Formatter &out,
+            size_t depth,
+            const std::string &name,
+            const std::string &sanitizedName,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode,
+            const std::string &parentName,
+            const std::string &offsetText) const override;
+
     bool needsEmbeddedReadWrite() const override;
+    bool needsResolveReferences() const override;
     bool resultNeedsDeref() const override;
 
     void emitJavaReaderWriter(
@@ -104,6 +127,8 @@
     std::vector<size_t> mSizes;
     std::vector<std::string> mSizeComments;
 
+    size_t dimension() const;
+
     DISALLOW_COPY_AND_ASSIGN(ArrayType);
 };
 
diff --git a/CompoundType.cpp b/CompoundType.cpp
index 8c1a131..11dd68a 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -236,6 +236,81 @@
         << offset
         << ");\n";
 }
+void CompoundType::emitResolveReferences(
+            Formatter &out,
+            const std::string &name,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode) const {
+    emitResolveReferencesEmbedded(
+        out,
+        0 /* depth */,
+        name,
+        name /* sanitizedName */,
+        nameIsPointer,
+        parcelObj,
+        parcelObjIsPointer,
+        isReader,
+        mode,
+        "_hidl_" + name + "_parent",
+        "0 /* parentOffset */");
+}
+
+void CompoundType::emitResolveReferencesEmbedded(
+            Formatter &out,
+            size_t /* depth */,
+            const std::string &name,
+            const std::string &/* sanitizedName */,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode,
+            const std::string &parentName,
+            const std::string &offsetText) const {
+    CHECK(needsResolveReferences());
+
+    const std::string parcelObjDeref =
+        parcelObjIsPointer ? ("*" + parcelObj) : parcelObj;
+
+    const std::string parcelObjPointer =
+        parcelObjIsPointer ? parcelObj : ("&" + parcelObj);
+
+    const std::string nameDeref = name + (nameIsPointer ? "->" : ".");
+    const std::string namePointer = nameIsPointer ? name : ("&" + name);
+
+    out << "_hidl_err = ";
+
+    if (isReader) {
+        out << "const_cast<"
+            << fullName()
+            << " *"
+            << ">("
+            << namePointer
+            << ")->readEmbeddedReferenceFromParcel(\n";
+    } else {
+        out << nameDeref
+            << "writeEmbeddedReferenceToParcel(\n";
+    }
+
+    out.indent();
+    out.indent();
+
+    out << (isReader ? parcelObjDeref : parcelObjPointer);
+    out << ",\n"
+        << parentName
+        << ",\n"
+        << offsetText;
+
+    out << ");\n\n";
+
+    out.unindent();
+    out.unindent();
+
+    handleError(out, mode);
+}
 
 status_t CompoundType::emitTypeDeclarations(Formatter &out) const {
     out << ((mStyle == STYLE_STRUCT) ? "struct" : "union")
@@ -282,6 +357,20 @@
         out.unindent();
     }
 
+    if(needsResolveReferences()) {
+        out << "\n";
+        out << "::android::status_t readEmbeddedReferenceFromParcel(\n";
+        out.indent(); out.indent();
+        out << "const ::android::hardware::Parcel &parcel,\n"
+            << "size_t parentHandle, size_t parentOffset);\n";
+        out.unindent(); out.unindent();
+        out << "::android::status_t writeEmbeddedReferenceToParcel(\n";
+        out.indent(); out.indent();
+        out << "::android::hardware::Parcel *,\n"
+            << "size_t parentHandle, size_t parentOffset) const;\n";
+        out.unindent(); out.unindent();
+    }
+
     out.unindent();
     out << "};\n\n";
 
@@ -296,12 +385,15 @@
         return err;
     }
 
-    if (!needsEmbeddedReadWrite()) {
-        return OK;
+    if (needsEmbeddedReadWrite()) {
+        emitStructReaderWriter(out, prefix, true /* isReader */);
+        emitStructReaderWriter(out, prefix, false /* isReader */);
     }
 
-    emitStructReaderWriter(out, prefix, true /* isReader */);
-    emitStructReaderWriter(out, prefix, false /* isReader */);
+    if (needsResolveReferences()) {
+        emitResolveReferenceDef(out, prefix, true /* isReader */);
+        emitResolveReferenceDef(out, prefix, false /* isReader */);
+    }
 
     return OK;
 }
@@ -556,6 +648,64 @@
     out << "}\n\n";
 }
 
+void CompoundType::emitResolveReferenceDef(
+        Formatter &out, const std::string prefix, bool isReader) const {
+    out << "::android::status_t "
+              << (prefix.empty() ? "" : (prefix + "::"))
+              << localName();
+
+    if (isReader) {
+        out << "::readEmbeddedReferenceFromParcel(\n";
+        out.indent(); out.indent();
+        out << "const ::android::hardware::Parcel &parcel,\n"
+            << "size_t parentHandle, size_t parentOffset)\n";
+        out.unindent(); out.unindent();
+    } else {
+        out << "::writeEmbeddedReferenceToParcel(\n";
+        out.indent(); out.indent();
+        out << "::android::hardware::Parcel *parcel,\n"
+            << "size_t parentHandle, size_t parentOffset) const\n";
+        out.unindent(); out.unindent();
+    }
+
+    out << " {\n";
+
+    out.indent();
+
+    out << "::android::status_t _hidl_err = ::android::OK;\n\n";
+
+    for (const auto &field : *mFields) {
+        if (!field->type().needsResolveReferences()) {
+            continue;
+        }
+
+        field->type().emitResolveReferencesEmbedded(
+            out,
+            0 /* depth */,
+            field->name(),
+            field->name() /* sanitizedName */,
+            false,    // nameIsPointer
+            "parcel", // const std::string &parcelObj,
+            !isReader, // bool parcelObjIsPointer,
+            isReader, // bool isReader,
+            ErrorMode_Return,
+            "parentHandle",
+            "parentOffset + offsetof("
+                + fullName()
+                + ", "
+                + field->name()
+                + ")"); // ErrorMode mode
+    }
+
+    out.unindent();
+    out << "_hidl_error:\n";
+    out.indent();
+    out << "return _hidl_err;\n";
+
+    out.unindent();
+    out << "}\n\n";
+}
+
 bool CompoundType::needsEmbeddedReadWrite() const {
     if (mStyle != STYLE_STRUCT) {
         return false;
@@ -570,6 +720,20 @@
     return false;
 }
 
+bool CompoundType::needsResolveReferences() const {
+    if (mStyle != STYLE_STRUCT) {
+        return false;
+    }
+
+    for (const auto &field : *mFields) {
+        if (field->type().needsResolveReferences()) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 bool CompoundType::resultNeedsDeref() const {
     return true;
 }
diff --git a/CompoundType.h b/CompoundType.h
index e905f08..325b25a 100644
--- a/CompoundType.h
+++ b/CompoundType.h
@@ -65,6 +65,28 @@
             const std::string &parentName,
             const std::string &offsetText) const override;
 
+    void emitResolveReferences(
+            Formatter &out,
+            const std::string &name,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode) const override;
+
+    void emitResolveReferencesEmbedded(
+            Formatter &out,
+            size_t depth,
+            const std::string &name,
+            const std::string &sanitizedName,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode,
+            const std::string &parentName,
+            const std::string &offsetText) const override;
+
     void emitJavaReaderWriter(
             Formatter &out,
             const std::string &parcelObj,
@@ -92,6 +114,7 @@
             Formatter &out, bool atTopLevel) const override;
 
     bool needsEmbeddedReadWrite() const override;
+    bool needsResolveReferences() const override;
     bool resultNeedsDeref() const override;
 
     status_t emitVtsTypeDeclarations(Formatter &out) const override;
@@ -107,6 +130,8 @@
 
     void emitStructReaderWriter(
             Formatter &out, const std::string &prefix, bool isReader) const;
+    void emitResolveReferenceDef(
+        Formatter &out, const std::string prefix, bool isReader) const;
 
     DISALLOW_COPY_AND_ASSIGN(CompoundType);
 };
diff --git a/RefType.cpp b/RefType.cpp
new file mode 100644
index 0000000..641f58e
--- /dev/null
+++ b/RefType.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "RefType.h"
+
+#include <hidl-util/Formatter.h>
+#include <android-base/logging.h>
+
+namespace android {
+
+RefType::RefType() {
+}
+
+/* return something like "T const *".
+ * The reason we don't return "const T *" is to handle cases like
+ * ref<ref<ref<T>>> t_3ptr;
+ * in this case the const's will get stacked on the left (const const const T *** t_3ptr)
+ * but in this implementation it would be clearer (T const* const* const* t_3ptr) */
+std::string RefType::getCppType(StorageMode /*mode*/, std::string *extra, bool /*specifyNamespaces*/) const {
+    const std::string elementBase = mElementType->getCppType(extra);
+    std::string base;
+    if(extra->empty())
+        base = elementBase + " const*";
+    else { // for pointer to array: e.g. int32_t (*meow)[5]
+        base = elementBase + " const(*";
+        extra->insert(0, 1, ')');
+    }
+
+    return base;
+}
+
+void RefType::addNamedTypesToSet(std::set<const FQName> &set) const {
+    mElementType->addNamedTypesToSet(set);
+}
+
+void RefType::emitReaderWriter(
+        Formatter &,
+        const std::string &,
+        const std::string &,
+        bool,
+        bool,
+        ErrorMode) const {
+    // RefType doesn't get read / written at this stage.
+    return;
+}
+
+void RefType::emitResolveReferences(
+            Formatter &out,
+            const std::string &name,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode) const {
+
+    emitResolveReferencesEmbedded(
+        out,
+        0 /* depth */,
+        name,
+        name /* sanitizedName */,
+        nameIsPointer,
+        parcelObj,
+        parcelObjIsPointer,
+        isReader,
+        mode,
+        "", // parentName
+        ""); // offsetText
+}
+
+void RefType::emitResolveReferencesEmbedded(
+            Formatter &out,
+            size_t /* depth */,
+            const std::string &name,
+            const std::string &sanitizedName,
+            bool /*nameIsPointer*/,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode,
+            const std::string &parentName,
+            const std::string &offsetText) const {
+
+    std::string elementExtra;
+    std::string elementBase = mElementType->getCppType(&elementExtra);
+    std::string elementType = elementBase + elementExtra;
+
+    std::string baseExtra;
+    std::string baseType = Type::getCppType(&baseExtra);
+
+    const std::string parcelObjDeref =
+        parcelObjIsPointer ? ("*" + parcelObj) : parcelObj;
+
+    const std::string parcelObjPointer =
+        parcelObjIsPointer ? parcelObj : ("&" + parcelObj);
+
+    // as if nameIsPointer is false. Pointers are always pass by values,
+    // so name is always the pointer value itself. Hence nameIsPointer = false.
+    const std::string namePointer = "&" + name;
+    const std::string handleName = "_hidl_" + sanitizedName + "__ref_handle";
+    const std::string resolveBufName = "_hidl_" + sanitizedName + "__ref_resolve_buf";
+
+    bool isEmbedded = (!parentName.empty() && !offsetText.empty());
+
+    out << "size_t " << handleName << ";\n"
+        << "bool " << resolveBufName << ";\n\n";
+
+    out << "_hidl_err = ";
+
+    if (isReader) {
+        out << "::android::hardware::read"
+            << (isEmbedded ? "Embedded" : "")
+            << "ReferenceFromParcel<"
+            << elementType
+            << ">(const_cast<"
+            << baseType
+            << " *"
+            << baseExtra
+            << ">("
+            << namePointer
+            << "),";
+    } else {
+        out << "::android::hardware::write"
+            << (isEmbedded ? "Embedded" : "")
+            << "ReferenceToParcel<"
+            << elementType
+            << ">("
+            << name
+            << ",";
+    }
+
+    out.indent();
+    out.indent();
+
+    out << (isReader ? parcelObjDeref : parcelObjPointer);
+    if(isEmbedded)
+        out << ",\n"
+            << parentName
+            << ",\n"
+            << offsetText;
+
+    out << ",\n&" + handleName;
+    out << ",\n&" + resolveBufName;
+    out << ");\n\n";
+
+    out.unindent();
+    out.unindent();
+
+    handleError(out, mode);
+
+    if(!mElementType->needsResolveReferences() && !mElementType->needsEmbeddedReadWrite())
+        return; // no need to deal with element type recursively.
+
+    out << "if(" << resolveBufName << ") {\n";
+    out.indent();
+
+    if(mElementType->needsEmbeddedReadWrite()) {
+        mElementType->emitReaderWriterEmbedded(
+            out,
+            0 /* depth */,
+            name,
+            true /* nameIsPointer */, // for element type, name is a pointer.
+            parcelObj,
+            parcelObjIsPointer,
+            isReader,
+            mode,
+            handleName,
+            "0 /* parentOffset */");
+    }
+
+    if(mElementType->needsResolveReferences()) {
+        mElementType->emitResolveReferencesEmbedded(
+            out,
+            0 /* depth */,
+            "(*" + name + ")",
+            sanitizedName + "_deref",
+            false /* nameIsPointer */,
+                // must deref it and say false here, otherwise pointer to pointers don't work
+            parcelObj,
+            parcelObjIsPointer,
+            isReader,
+            mode,
+            handleName,
+            "0 /* parentOffset */");
+    }
+
+    out.unindent();
+    out << "}\n\n";
+}
+
+
+
+
+bool RefType::needsResolveReferences() const {
+    return true;
+}
+
+bool RefType::needsEmbeddedReadWrite() const {
+    return false;
+}
+
+bool RefType::resultNeedsDeref() const {
+    return false;
+}
+
+status_t RefType::emitVtsTypeDeclarations(Formatter &out) const {
+    out << "type: TYPE_REF\n" << "ref_value: {\n";
+    out.indent();
+    status_t err = mElementType->emitVtsTypeDeclarations(out);
+    if (err != OK) {
+        return err;
+    }
+    out.unindent();
+    out << "}\n";
+    return OK;
+}
+
+status_t RefType::emitVtsAttributeType(Formatter &out) const {
+    out << "type: TYPE_REF\n" << "ref_value: {\n";
+    out.indent();
+    status_t status = mElementType->emitVtsAttributeType(out);
+    if (status != OK) {
+        return status;
+    }
+    out.unindent();
+    out << "}\n";
+    return OK;
+}
+
+bool RefType::isJavaCompatible() const {
+    return false;
+}
+
+}  // namespace android
+
diff --git a/RefType.h b/RefType.h
new file mode 100644
index 0000000..153ddb3
--- /dev/null
+++ b/RefType.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef REF_TYPE_H_
+
+#define REF_TYPE_H_
+
+#include "Type.h"
+
+namespace android {
+
+struct RefType : public TemplatedType {
+    RefType();
+
+    void addNamedTypesToSet(std::set<const FQName> &set) const override;
+    std::string getCppType(StorageMode mode, std::string *extra,
+                           bool specifyNamespaces) const override;
+
+    void emitReaderWriter(
+            Formatter &out,
+            const std::string &name,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode) const override;
+
+    void emitResolveReferences(
+            Formatter &out,
+            const std::string &name,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode) const override;
+
+    void emitResolveReferencesEmbedded(
+            Formatter &out,
+            size_t depth,
+            const std::string &name,
+            const std::string &sanitizedName,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode,
+            const std::string &parentName,
+            const std::string &offsetText) const override;
+
+    bool needsEmbeddedReadWrite() const override;
+    bool needsResolveReferences() const override;
+    bool resultNeedsDeref() const override;
+
+    status_t emitVtsTypeDeclarations(Formatter &out) const override;
+    status_t emitVtsAttributeType(Formatter &out) const override;
+
+    bool isJavaCompatible() const override;
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(RefType);
+};
+
+}  // namespace android
+
+#endif  // REF_TYPE_H_
+
diff --git a/Type.cpp b/Type.cpp
index e54fdc5..0b2b715 100644
--- a/Type.cpp
+++ b/Type.cpp
@@ -120,6 +120,32 @@
     CHECK(!"Should not be here");
 }
 
+void Type::emitResolveReferences(
+        Formatter &,
+        const std::string &,
+        bool,
+        const std::string &,
+        bool,
+        bool,
+        ErrorMode) const {
+    CHECK(!"Should not be here");
+}
+
+void Type::emitResolveReferencesEmbedded(
+        Formatter &,
+        size_t,
+        const std::string &,
+        const std::string &,
+        bool,
+        const std::string &,
+        bool,
+        bool,
+        ErrorMode,
+        const std::string &,
+        const std::string &) const {
+    CHECK(!"Should not be here");
+}
+
 void Type::emitReaderWriterEmbedded(
         Formatter &,
         size_t,
@@ -298,6 +324,10 @@
     return false;
 }
 
+bool Type::needsResolveReferences() const {
+    return false;
+}
+
 bool Type::resultNeedsDeref() const {
     return false;
 }
@@ -356,5 +386,14 @@
     CHECK(!"Should not be here");
 }
 
+////////////////////////////////////////
+
+TemplatedType::TemplatedType() : mElementType(nullptr) {
+}
+void TemplatedType::setElementType(Type *elementType) {
+    CHECK(mElementType == nullptr); // can only be set once.
+    mElementType = elementType;
+}
+
 }  // namespace android
 
diff --git a/Type.h b/Type.h
index 5a82718..ce0d78f 100644
--- a/Type.h
+++ b/Type.h
@@ -118,6 +118,28 @@
             const std::string &parentName,
             const std::string &offsetText) const;
 
+    virtual void emitResolveReferences(
+            Formatter &out,
+            const std::string &name,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode) const;
+
+    virtual void emitResolveReferencesEmbedded(
+            Formatter &out,
+            size_t depth,
+            const std::string &name,
+            const std::string &sanitizedName,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode,
+            const std::string &parentName,
+            const std::string &offsetText) const;
+
     virtual void emitJavaReaderWriter(
             Formatter &out,
             const std::string &parcelObj,
@@ -146,6 +168,7 @@
             Formatter &out, bool atTopLevel) const;
 
     virtual bool needsEmbeddedReadWrite() const;
+    virtual bool needsResolveReferences() const;
     virtual bool resultNeedsDeref() const;
 
     // Generates type declaration for vts proto file.
@@ -194,6 +217,16 @@
     DISALLOW_COPY_AND_ASSIGN(Type);
 };
 
+/* Base type for VectorType and RefType. */
+struct TemplatedType : public Type {
+    void setElementType(Type *elementType);
+protected:
+    TemplatedType();
+    Type *mElementType;
+private:
+    DISALLOW_COPY_AND_ASSIGN(TemplatedType);
+};
+
 }  // namespace android
 
 #endif  // TYPE_H_
diff --git a/VectorType.cpp b/VectorType.cpp
index adf7967..8bf3ede 100644
--- a/VectorType.cpp
+++ b/VectorType.cpp
@@ -23,8 +23,7 @@
 
 namespace android {
 
-VectorType::VectorType(Type *elementType)
-    : mElementType(elementType) {
+VectorType::VectorType() {
 }
 
 void VectorType::addNamedTypesToSet(std::set<const FQName> &set) const {
@@ -34,6 +33,9 @@
 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 base =
           std::string(specifyNamespaces ? "::android::hardware::" : "")
         + "hidl_vec<"
@@ -207,6 +209,111 @@
     out << "}\n\n";
 }
 
+void VectorType::emitResolveReferences(
+            Formatter &out,
+            const std::string &name,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode) const {
+    emitResolveReferencesEmbeddedHelper(
+        out,
+        0, /* depth */
+        name,
+        name /* sanitizedName */,
+        nameIsPointer,
+        parcelObj,
+        parcelObjIsPointer,
+        isReader,
+        mode,
+        "_hidl_" + name + "_child",
+        "0 /* parentOffset */");
+}
+
+void VectorType::emitResolveReferencesEmbedded(
+            Formatter &out,
+            size_t depth,
+            const std::string &name,
+            const std::string &sanitizedName,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode,
+            const std::string & /* parentName */,
+            const std::string & /* offsetText */) const {
+    emitResolveReferencesEmbeddedHelper(
+        out, depth, name, sanitizedName, nameIsPointer, parcelObj,
+        parcelObjIsPointer, isReader, mode, "", "");
+}
+
+void VectorType::emitResolveReferencesEmbeddedHelper(
+            Formatter &out,
+            size_t depth,
+            const std::string &name,
+            const std::string &sanitizedName,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode,
+            const std::string &childName,
+            const std::string &childOffsetText) const {
+    CHECK(needsResolveReferences() && mElementType->needsResolveReferences());
+
+    const std::string nameDeref = name + (nameIsPointer ? "->" : ".");
+    std::string elementExtra;
+    std::string elementType = mElementType->getCppType(&elementExtra);
+
+    std::string myChildName = childName, myChildOffset = childOffsetText;
+
+    if(myChildName.empty() && myChildOffset.empty()) {
+        myChildName = "_hidl_" + sanitizedName + "_child";
+        myChildOffset = "0";
+
+        out << "size_t " << myChildName << ";\n";
+        out << "_hidl_err = " << nameDeref << "findInParcel("
+            << (parcelObjIsPointer ? "*" : "") << parcelObj << ", "
+            << "&" << myChildName
+            << ");\n";
+
+        handleError(out, mode);
+    }
+
+    std::string iteratorName = "_hidl_index_" + std::to_string(depth);
+
+    out << "for (size_t "
+        << iteratorName
+        << " = 0; "
+        << iteratorName
+        << " < "
+        << nameDeref
+        << "size(); ++"
+        << iteratorName
+        << ") {\n";
+
+    out.indent();
+
+    mElementType->emitResolveReferencesEmbedded(
+        out,
+        depth + 1,
+        (nameIsPointer ? "(*" + name + ")" : name) + "[" + iteratorName + "]",
+        sanitizedName + (nameIsPointer ? "_deref" : "") + "_indexed",
+        false /* nameIsPointer */,
+        parcelObj,
+        parcelObjIsPointer,
+        isReader,
+        mode,
+        myChildName,
+        myChildOffset + " + " +
+                iteratorName + " * sizeof(" + elementType + elementExtra + ")");
+
+    out.unindent();
+
+    out << "}\n\n";
+}
+
 void VectorType::emitJavaReaderWriter(
         Formatter &out,
         const std::string &parcelObj,
@@ -426,6 +533,10 @@
     return true;
 }
 
+bool VectorType::needsResolveReferences() const {
+    return mElementType->needsResolveReferences();
+}
+
 bool VectorType::resultNeedsDeref() const {
     return true;
 }
diff --git a/VectorType.h b/VectorType.h
index b925d98..3f3e7d3 100644
--- a/VectorType.h
+++ b/VectorType.h
@@ -22,8 +22,8 @@
 
 namespace android {
 
-struct VectorType : public Type {
-    VectorType(Type *elementType);
+struct VectorType : public TemplatedType {
+    VectorType();
 
     void addNamedTypesToSet(std::set<const FQName> &set) const override;
 
@@ -55,6 +55,28 @@
             const std::string &parentName,
             const std::string &offsetText) const override;
 
+    void emitResolveReferences(
+            Formatter &out,
+            const std::string &name,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode) const override;
+
+    void emitResolveReferencesEmbedded(
+            Formatter &out,
+            size_t depth,
+            const std::string &name,
+            const std::string &sanitizedName,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode,
+            const std::string &parentName,
+            const std::string &offsetText) const override;
+
     void emitJavaReaderWriter(
             Formatter &out,
             const std::string &parcelObj,
@@ -84,6 +106,7 @@
             bool isReader);
 
     bool needsEmbeddedReadWrite() const override;
+    bool needsResolveReferences() const override;
     bool resultNeedsDeref() const override;
 
     status_t emitVtsTypeDeclarations(Formatter &out) const override;
@@ -94,7 +117,27 @@
     void getAlignmentAndSize(size_t *align, size_t *size) const override;
 
  private:
-    Type *mElementType;
+    // Helper method for emitResolveReferences[Embedded].
+    // Pass empty childName and childOffsetText if the original
+    // childHandle is unknown.
+    // For example, given a vec<ref<T>> (T is a simple struct that
+    // contains primitive values only), then the following methods are
+    // invoked:
+    // 1. VectorType::emitResolveReferences
+    //    ... which calls the helper with empty childName and childOffsetText
+    // 2. RefType::emitResolveReferencesEmbedded
+    void emitResolveReferencesEmbeddedHelper(
+            Formatter &out,
+            size_t depth,
+            const std::string &name,
+            const std::string &sanitizedName,
+            bool nameIsPointer,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode,
+            const std::string &childName,
+            const std::string &childOffsetText) const;
 
     DISALLOW_COPY_AND_ASSIGN(VectorType);
 };
diff --git a/generateCpp.cpp b/generateCpp.cpp
index 9968635..3b9320a 100644
--- a/generateCpp.cpp
+++ b/generateCpp.cpp
@@ -894,6 +894,27 @@
             mode);
 }
 
+void AST::emitCppResolveReferences(
+        Formatter &out,
+        const std::string &parcelObj,
+        bool parcelObjIsPointer,
+        const TypedVar *arg,
+        bool isReader,
+        Type::ErrorMode mode,
+        bool addPrefixToName) const {
+    const Type &type = arg->type();
+    if(type.needsResolveReferences()) {
+        type.emitResolveReferences(
+                out,
+                addPrefixToName ? ("_hidl_out_" + arg->name()) : arg->name(),
+                isReader, // nameIsPointer
+                parcelObj,
+                parcelObjIsPointer,
+                isReader,
+                mode);
+    }
+}
+
 status_t AST::generateProxySource(
         Formatter &out, const std::string &baseName) const {
     const std::string klassName = "Bp" + baseName;
@@ -954,6 +975,7 @@
 
             out << "if (_hidl_err != ::android::OK) { goto _hidl_error; }\n\n";
 
+            // First DFS: write all buffers and resolve pointers for parent
             for (const auto &arg : method->args()) {
                 emitCppReaderWriter(
                         out,
@@ -965,6 +987,18 @@
                         false /* addPrefixToName */);
             }
 
+            // Second DFS: resolve references.
+            for (const auto &arg : method->args()) {
+                emitCppResolveReferences(
+                        out,
+                        "_hidl_data",
+                        false /* parcelObjIsPointer */,
+                        arg,
+                        false /* reader */,
+                        Type::ErrorMode_Goto,
+                        false /* addPrefixToName */);
+            }
+
             out << "_hidl_err = remote()->transact("
                       << superInterface->fqName().cppNamespace()
                       << "::IHw"
@@ -984,6 +1018,8 @@
                 out << "if (_hidl_err != ::android::OK) { goto _hidl_error; }\n\n";
                 out << "if (!_hidl_status.isOk()) { return _hidl_status; }\n\n";
 
+
+                // First DFS: write all buffers and resolve pointers for parent
                 for (const auto &arg : method->results()) {
                     emitCppReaderWriter(
                             out,
@@ -995,6 +1031,18 @@
                             true /* addPrefixToName */);
                 }
 
+                // Second DFS: resolve references.
+                for (const auto &arg : method->results()) {
+                    emitCppResolveReferences(
+                            out,
+                            "_hidl_reply",
+                            false /* parcelObjIsPointer */,
+                            arg,
+                            true /* reader */,
+                            Type::ErrorMode_Goto,
+                            true /* addPrefixToName */);
+                }
+
                 if (returnsValue && elidedReturn == nullptr) {
                     out << "if (_hidl_cb != nullptr) {\n";
                     out.indent();
@@ -1204,6 +1252,7 @@
 
     declareCppReaderLocals(out, method->args(), false /* forResults */);
 
+    // First DFS: write buffers
     for (const auto &arg : method->args()) {
         emitCppReaderWriter(
                 out,
@@ -1215,6 +1264,18 @@
                 false /* addPrefixToName */);
     }
 
+    // Second DFS: resolve references
+    for (const auto &arg : method->args()) {
+        emitCppResolveReferences(
+                out,
+                "_hidl_data",
+                false /* parcelObjIsPointer */,
+                arg,
+                true /* reader */,
+                Type::ErrorMode_Break,
+                false /* addPrefixToName */);
+    }
+
     out << "if (UNLIKELY(enableInstrumentation)) {\n";
     out.indent();
     out << "std::vector<void *> args;\n";
@@ -1273,6 +1334,15 @@
                 false, /* isReader */
                 Type::ErrorMode_Ignore);
 
+        emitCppResolveReferences(
+                out,
+                "_hidl_reply",
+                true /* parcelObjIsPointer */,
+                elidedReturn,
+                false /* reader */,
+                Type::ErrorMode_Ignore,
+                false /* addPrefixToName */);
+
         out << "if (UNLIKELY(enableInstrumentation)) {\n";
         out.indent();
         out << "std::vector<void *> results;\n";
@@ -1338,6 +1408,7 @@
             out << "::android::hardware::Status::ok()"
                       << ".writeToParcel(_hidl_reply);\n\n";
 
+            // First DFS: buffers
             for (const auto &arg : method->results()) {
                 emitCppReaderWriter(
                         out,
@@ -1349,6 +1420,18 @@
                         false /* addPrefixToName */);
             }
 
+            // Second DFS: resolve references
+            for (const auto &arg : method->results()) {
+                emitCppResolveReferences(
+                        out,
+                        "_hidl_reply",
+                        true /* parcelObjIsPointer */,
+                        arg,
+                        false /* reader */,
+                        Type::ErrorMode_Ignore,
+                        false /* addPrefixToName */);
+            }
+
             out << "if (UNLIKELY(enableInstrumentation)) {\n";
             out.indent();
             out << "std::vector<void *> results;\n";
diff --git a/hidl-gen_l.ll b/hidl-gen_l.ll
index 8a52137..97e3737 100644
--- a/hidl-gen_l.ll
+++ b/hidl-gen_l.ll
@@ -38,6 +38,8 @@
 #include "Method.h"
 #include "ScalarType.h"
 #include "StringType.h"
+#include "VectorType.h"
+#include "RefType.h"
 
 #include "hidl-gen_y.h"
 
@@ -88,7 +90,8 @@
 "struct"		{ return token::STRUCT; }
 "typedef"		{ return token::TYPEDEF; }
 "union"			{ return token::UNION; }
-"vec"			{ return token::VEC; }
+"vec"			{ yylval->templatedType = new VectorType; return token::TEMPLATED; }
+"ref"			{ yylval->templatedType = new RefType; return token::TEMPLATED; }
 "oneway"		{ return token::ONEWAY; }
 
 "bool"			{ SCALAR_TYPE(KIND_BOOL); }
diff --git a/hidl-gen_y.yy b/hidl-gen_y.yy
index 0e7483e..c494643 100644
--- a/hidl-gen_y.yy
+++ b/hidl-gen_y.yy
@@ -27,6 +27,7 @@
 #include "Interface.h"
 #include "Method.h"
 #include "VectorType.h"
+#include "RefType.h"
 
 #include "hidl-gen_y.h"
 
@@ -70,7 +71,7 @@
 %token<str> STRING_LITERAL
 %token<str> TYPEDEF
 %token<str> UNION
-%token<str> VEC
+%token<templatedType> TEMPLATED
 %token<void> ONEWAY
 
 /* Operator precedence and associativity, as per
@@ -130,6 +131,7 @@
 %union {
     const char *str;
     android::Type *type;
+    android::TemplatedType *templatedType;
     android::FQName *fqName;
     android::CompoundType *compoundType;
     android::CompoundField *field;
@@ -730,7 +732,7 @@
 
 type
     : fqtype { $$ = $1; }
-    | fqtype '[' const_expr ']'
+    | type '[' const_expr ']'
       {
           if ($1->isBinder()) {
               std::cerr << "ERROR: Arrays of interface types are not supported."
@@ -745,16 +747,28 @@
               $$ = new ArrayType($1, $3);
           }
       }
-    | VEC '<' fqtype '>'
+    | TEMPLATED '<' type '>'
       {
           if ($3->isBinder()) {
-              std::cerr << "ERROR: Vectors of interface types are not "
+              std::cerr << "ERROR: TemplatedType of interface types are not "
                         << "supported. at " << @3 << "\n";
 
               YYERROR;
           }
+          $1->setElementType($3);
+          $$ = $1;
+      }
+    | TEMPLATED '<' TEMPLATED '<' type RSHIFT
+      {
+          if ($5->isBinder()) {
+              std::cerr << "ERROR: TemplatedType of interface types are not "
+                        << "supported. at " << @5 << "\n";
 
-          $$ = new VectorType($3);
+              YYERROR;
+          }
+          $3->setElementType($5);
+          $1->setElementType($3);
+          $$ = $1;
       }
     | annotated_compound_declaration { $$ = $1; }
     | INTERFACE { $$ = new GenericBinder; }
diff --git a/test/Android.mk b/test/Android.mk
index 6ea596b..65a0497 100644
--- a/test/Android.mk
+++ b/test/Android.mk
@@ -18,6 +18,7 @@
     libutils                    	\
     android.hardware.tests.foo@1.0	\
     android.hardware.tests.bar@1.0	\
+    android.hardware.tests.pointer@1.0	\
 
 LOCAL_STATIC_LIBRARIES := \
     libgtest
diff --git a/test/main.cpp b/test/main.cpp
index 59c9604..28606ee 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -4,6 +4,8 @@
 #include <android/hardware/tests/foo/1.0/BnFoo.h>
 #include <android/hardware/tests/foo/1.0/BnFooCallback.h>
 #include <android/hardware/tests/bar/1.0/BnBar.h>
+#include <android/hardware/tests/pointer/1.0/BnGraph.h>
+#include <android/hardware/tests/pointer/1.0/BnPointer.h>
 
 #include <gtest/gtest.h>
 #include <inttypes.h>
@@ -16,6 +18,9 @@
 #error "GTest did not detect pthread library."
 #endif
 
+#include <vector>
+#include <sstream>
+
 #include <hidl/IServiceManager.h>
 #include <hidl/Status.h>
 #include <hwbinder/IPCThreadState.h>
@@ -24,14 +29,20 @@
 #include <utils/Condition.h>
 #include <utils/Timers.h>
 
-using ::android::hardware::tests::foo::V1_0::BnFoo;
-using ::android::hardware::tests::foo::V1_0::BnFooCallback;
-using ::android::hardware::tests::bar::V1_0::BnBar;
+#define PUSH_ERROR_IF(__cond__) if(__cond__) { errors.push_back(std::to_string(__LINE__) + ": " + #__cond__); }
+#define EXPECT_OK(__ret__) EXPECT_TRUE(isOk(__ret__))
+#define EXPECT_ARRAYEQ(__a1__, __a2__, __size__) EXPECT_TRUE(IsArrayEq(__a1__, __a2__, __size__))
+
+// TODO uncomment this when kernel is patched with pointer changes.
+//#define HIDL_RUN_POINTER_TESTS 1
+
 using ::android::hardware::tests::foo::V1_0::IFoo;
 using ::android::hardware::tests::foo::V1_0::IFooCallback;
 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::Return;
 using ::android::hardware::Void;
 using ::android::hardware::hidl_array;
@@ -41,6 +52,426 @@
 using ::android::Mutex;
 using ::android::Condition;
 
+template <typename T>
+static inline ::testing::AssertionResult isOk(::android::hardware::Return<T> ret) {
+    return ret.getStatus().isOk()
+        ? (::testing::AssertionSuccess() << ret.getStatus())
+        : (::testing::AssertionFailure() << ret.getStatus());
+}
+
+template<typename T, typename S>
+static inline bool isArrayEqual(const T arr1, const S arr2, size_t size) {
+    for(size_t i = 0; i < size; i++)
+        if(arr1[i] != arr2[i])
+            return false;
+    return true;
+}
+
+static void simpleGraph(IGraph::Graph& g) {
+    g.nodes.resize(2);
+    g.edges.resize(1);
+    g.nodes[0].data = 10;
+    g.nodes[1].data = 20;
+    g.edges[0].left = &g.nodes[0];
+    g.edges[0].right = &g.nodes[1];
+}
+static bool isSimpleGraph(const IGraph::Graph &g) {
+    if(g.nodes.size() != 2) return false;
+    if(g.edges.size() != 1) return false;
+    if(g.nodes[0].data != 10) return false;
+    if(g.nodes[1].data != 20) return false;
+    if(g.edges[0].left != &g.nodes[0]) return false;
+    if(g.edges[0].right != &g.nodes[1]) return false;
+    return true;
+}
+
+static void logSimpleGraph(const char *prefix, const IGraph::Graph& g) {
+    ALOGI("%s Graph %p, %d nodes, %d edges", prefix, &g, (int)g.nodes.size(), (int)g.edges.size());
+    std::ostringstream os;
+    for(size_t i = 0; i < g.nodes.size(); i++)
+      os << &g.nodes[i] << " = " << g.nodes[i].data << ", ";
+    ALOGI("%s Nodes: [%s]", prefix, os.str().c_str());
+    os.str("");
+    os.clear();
+    for(size_t i = 0; i < g.edges.size(); i++)
+      os << g.edges[i].left << " -> " << g.edges[i].right << ", ";
+    ALOGI("%s Edges: [%s]", prefix, os.str().c_str());
+}
+
+struct GraphInterface : public IGraph {
+    Return<void> passAGraph(const Graph& e) override;
+    Return<void> giveAGraph(giveAGraph_cb _cb) override;
+    Return<void> passANode(const IGraph::Node& n) override;
+    Return<void> passTwoGraphs(IGraph::Graph const* g1, IGraph::Graph const* g2) override;
+    Return<void> passAGamma(const IGraph::Gamma& c) override;
+    Return<void> passASimpleRef(const IGraph::Alpha * a) override;
+    Return<void> giveASimpleRef(giveASimpleRef_cb _hidl_cb) override;
+    Return<void> passASimpleRefS(const IGraph::Theta * s) override;
+    Return<int32_t> getErrors() override;
+private:
+    std::vector<std::string> errors;
+};
+
+Return<void> GraphInterface::passAGraph(const Graph& g) {
+    ALOGI("SERVER(Graph) passAGraph start.");
+    PUSH_ERROR_IF(!isSimpleGraph(g));
+    // logSimpleGraph("SERVER(Graph) passAGraph:", g);
+    return Void();
+}
+
+Return<void> GraphInterface::giveAGraph(giveAGraph_cb _cb) {
+    IGraph::Graph g;
+    simpleGraph(g);
+    _cb(g);
+    return Void();
+}
+
+Return<void> GraphInterface::passANode(const IGraph::Node& n) {
+    PUSH_ERROR_IF(n.data != 10);
+    return Void();
+}
+
+Return<void> GraphInterface::passTwoGraphs(IGraph::Graph const* g1, IGraph::Graph const* g2) {
+    PUSH_ERROR_IF(g1 != g2);
+    PUSH_ERROR_IF(!isSimpleGraph(*g1));
+    logSimpleGraph("SERVER(Graph): passTwoGraphs", *g2);
+    return Void();
+}
+
+Return<void> GraphInterface::passAGamma(const IGraph::Gamma& c) {
+    if(c.a_ptr == nullptr && c.b_ptr == nullptr)
+      return Void();
+    ALOGI("SERVER(Graph) passAGamma received c.a = %p, c.b = %p, c.a->s = %p, c.b->s = %p",
+        c.a_ptr, c.b_ptr, c.a_ptr->s_ptr, c.b_ptr->s_ptr);
+    ALOGI("SERVER(Graph) passAGamma received data %d, %d",
+        (int)c.a_ptr->s_ptr->data, (int)c.b_ptr->s_ptr->data);
+    PUSH_ERROR_IF(c.a_ptr->s_ptr != c.b_ptr->s_ptr);
+    return Void();
+}
+Return<void> GraphInterface::passASimpleRef(const IGraph::Alpha * a_ptr) {
+    ALOGI("SERVER(Graph) passASimpleRef received %d", a_ptr->s_ptr->data);
+    PUSH_ERROR_IF(a_ptr->s_ptr->data != 500);
+    return Void();
+}
+Return<void> GraphInterface::passASimpleRefS(const IGraph::Theta * s_ptr) {
+    ALOGI("SERVER(Graph) passASimpleRefS received %d @ %p", s_ptr->data, s_ptr);
+    PUSH_ERROR_IF(s_ptr->data == 10);
+    return Void();
+}
+Return<void> GraphInterface::giveASimpleRef(giveASimpleRef_cb _cb) {
+    IGraph::Theta s; s.data = 500;
+    IGraph::Alpha a; a.s_ptr = &s;
+    _cb(&a);
+    return Void();
+}
+
+Return<int32_t> GraphInterface::getErrors() {
+    if(!errors.empty()) {
+        for(const auto& e : errors)
+            ALOGW("SERVER(Graph) error: %s", e.c_str());
+    }
+    return errors.size();
+}
+
+struct PointerInterface : public IPointer {
+private:
+    std::vector<std::string> errors;
+public:
+    Return<int32_t> getErrors() {
+        if(!errors.empty()) {
+            for(const auto& e : errors)
+                ALOGW("SERVER(Pointer) error: %s", e.c_str());
+        }
+        return errors.size();
+    }
+    Return<void> foo1(const IPointer::Sam& s, IPointer::Sam const* s_ptr) override {
+        PUSH_ERROR_IF(!(&s == s_ptr));
+        return Void();
+    }
+    Return<void> foo2(const IPointer::Sam& s, const IPointer::Ada& a) override {
+        PUSH_ERROR_IF(!(&s == a.s_ptr));
+        return Void();
+    }
+    Return<void> foo3(const IPointer::Sam& s, const IPointer::Ada& a, const IPointer::Bob& b) override {
+        PUSH_ERROR_IF(!(&a == b.a_ptr && a.s_ptr == b.s_ptr && a.s_ptr == &s));
+        return Void();
+    }
+    Return<void> foo4(IPointer::Sam const* s_ptr) override {
+        PUSH_ERROR_IF(!(s_ptr->data == 500));
+        return Void();
+    }
+    Return<void> foo5(const IPointer::Ada& a, const IPointer::Bob& b) override {
+        PUSH_ERROR_IF(!(a.s_ptr == b.s_ptr && b.a_ptr == &a));
+        return Void();
+    }
+    Return<void> foo6(IPointer::Ada const* a_ptr) override {
+        PUSH_ERROR_IF(!(a_ptr->s_ptr->data == 500));
+        return Void();
+    }
+    Return<void> foo7(IPointer::Ada const* a_ptr, IPointer::Bob const* b_ptr) override {
+        PUSH_ERROR_IF(!(a_ptr->s_ptr == b_ptr->s_ptr && a_ptr == b_ptr->a_ptr && a_ptr->s_ptr->data == 500));
+        return Void();
+    }
+    Return<void> foo8(const IPointer::Dom& d) override {
+        const IPointer::Cin& c = d.c;
+        PUSH_ERROR_IF(&c.a != c.b_ptr->a_ptr);
+        PUSH_ERROR_IF(c.a.s_ptr != c.b_ptr->s_ptr);
+        PUSH_ERROR_IF(c.a.s_ptr->data != 500);
+        return Void();
+    }
+    Return<void> foo9(::android::hardware::hidl_string const* str_ref) override {
+        ALOGI("SERVER(Pointer) foo9 got string @ %p (size = %ld) \"%s\"",
+            str_ref->c_str(),
+            (unsigned long) str_ref->size(), str_ref->c_str());
+        PUSH_ERROR_IF(!(strcmp(str_ref->c_str(), "meowmeowmeow") == 0));
+        return Void();
+    }
+    Return<void> foo10(const ::android::hardware::hidl_vec<IPointer::Sam const*>& s_ptr_vec) override {
+        PUSH_ERROR_IF(s_ptr_vec[0]->data != 500);
+        if(s_ptr_vec.size() != 5) {
+            errors.push_back("foo10: s_ptr_vec.size() != 5");
+            return Void();
+        }
+        for(size_t i = 0; i < s_ptr_vec.size(); i++)
+            PUSH_ERROR_IF(s_ptr_vec[0] != s_ptr_vec[i]);
+        return Void();
+    }
+    Return<void> foo11(::android::hardware::hidl_vec<IPointer::Sam> const* s_vec_ptr) override {
+        if(s_vec_ptr->size() != 5) {
+            errors.push_back("foo11: s_vec_ptr->size() != 5");
+            return Void();
+        }
+        for(size_t i = 0; i < 5; i++)
+            PUSH_ERROR_IF((*s_vec_ptr)[i].data != 500);
+        return Void();
+    }
+    Return<void> foo12(hidl_array<IPointer::Sam, 5> const* s_array_ref) override {
+        for(size_t i = 0; i < 5; ++i)
+            PUSH_ERROR_IF((*s_array_ref)[i].data != 500);
+        return Void();
+    }
+    Return<void> foo13(const hidl_array<IPointer::Sam const*, 5>& s_ref_array) override {
+        PUSH_ERROR_IF(s_ref_array[0]->data != 500)
+        for(size_t i = 0; i < 5; i++)
+            PUSH_ERROR_IF(s_ref_array[i] != s_ref_array[0])
+        return Void();
+    }
+    Return<void> foo14(IPointer::Sam const* const* const* s_3ptr) override {
+        PUSH_ERROR_IF(!((***s_3ptr).data == 500))
+        return Void();
+    }
+    Return<void> foo15(int32_t const* const* const* i_3ptr) override {
+        PUSH_ERROR_IF(!((***i_3ptr) == 500))
+        return Void();
+    }
+
+    Return<void> foo16(const IPointer::Ptr& p) override {
+        PUSH_ERROR_IF((*p.array_ptr)[0].s_ptr->data != 500);
+        for(size_t i = 0; i < 5; i++) PUSH_ERROR_IF((*p.array_ptr)[i].s_ptr != (*p.array_ptr)[0].s_ptr);
+        PUSH_ERROR_IF(*(p.int_ptr) != 500);
+        for(size_t i = 0; i < 5; i++) PUSH_ERROR_IF((*p.int_array_ptr)[i] != 500);
+        for(size_t i = 0; i < 5; i++) PUSH_ERROR_IF(p.int_ptr_array[i] != p.int_ptr);
+        PUSH_ERROR_IF(p.a_ptr_vec.size() != 5);
+        PUSH_ERROR_IF(p.a_ptr_vec[0]->s_ptr->data != 500);
+        for(size_t i = 0; i < 5; i++) PUSH_ERROR_IF(p.a_ptr_vec[i]->s_ptr != p.a_ptr_vec[0]->s_ptr);
+        PUSH_ERROR_IF(strcmp(p.str_ref->c_str(), "meowmeowmeow") != 0);
+        PUSH_ERROR_IF(p.a_vec_ptr->size() != 5);
+        PUSH_ERROR_IF((*p.a_vec_ptr)[0].s_ptr->data != 500);
+        for(size_t i = 0; i < 5; i++) PUSH_ERROR_IF((*p.a_vec_ptr)[i].s_ptr != (*p.a_vec_ptr)[0].s_ptr);
+        return Void();
+    };
+    Return<void> foo17(IPointer::Ptr const* p) override {
+        return foo16(*p);
+    };
+    Return<void> foo18(hidl_string const* str_ref, hidl_string const* str_ref2, const hidl_string& str) override {
+        PUSH_ERROR_IF(&str != str_ref);
+        PUSH_ERROR_IF(str_ref != str_ref2);
+        PUSH_ERROR_IF(strcmp(str.c_str(), "meowmeowmeow") != 0)
+        return Void();
+    };
+    Return<void> foo19(
+                hidl_vec<IPointer::Ada> const* a_vec_ref,
+                const hidl_vec<IPointer::Ada>& a_vec,
+                hidl_vec<IPointer::Ada> const* a_vec_ref2) {
+        PUSH_ERROR_IF(&a_vec != a_vec_ref);
+        PUSH_ERROR_IF(a_vec_ref2 != a_vec_ref);
+        PUSH_ERROR_IF(a_vec.size() != 5);
+        PUSH_ERROR_IF(a_vec[0].s_ptr->data != 500);
+        for(size_t i = 0; i < 5; i++)
+            PUSH_ERROR_IF(a_vec[i].s_ptr != a_vec[0].s_ptr);
+        return Void();
+    };
+
+    Return<void> foo21(hidl_array<IPointer::Ada, 3, 2, 1> const* a_array_ptr) override {
+        const hidl_array<IPointer::Ada, 3, 2, 1>& a_array = *a_array_ptr;
+        PUSH_ERROR_IF(a_array[0][0][0].s_ptr->data != 500);
+        for(size_t i = 0; i < 3; i++)
+            for(size_t j = 0; j < 2; j++)
+                for(size_t k = 0; k < 1; k++)
+                    PUSH_ERROR_IF(a_array[i][j][k].s_ptr != a_array[0][0][0].s_ptr);
+        return Void();
+    }
+    Return<void> foo22(const hidl_array<IPointer::Ada const*, 3, 2, 1>& a_ptr_array) override {
+        PUSH_ERROR_IF(a_ptr_array[0][0][0]->s_ptr->data != 500);
+        for(size_t i = 0; i < 3; i++)
+            for(size_t j = 0; j < 2; j++)
+                for(size_t k = 0; k < 1; k++)
+                    PUSH_ERROR_IF(a_ptr_array[i][j][k] != a_ptr_array[0][0][0]);
+        return Void();
+    }
+
+    IPointer::Sam *s;
+    IPointer::Ada *a;
+    IPointer::Bob *b;
+    IPointer::Cin *c;
+    IPointer::Dom *d;
+
+    IPointer::Ptr p;
+    hidl_array<IPointer::Ada, 5> a_array;
+    int32_t someInt;
+    hidl_array<int32_t, 5> someIntArray;
+    hidl_string str;
+    hidl_vec<IPointer::Ada> a_vec;
+    PointerInterface() {
+        d = new IPointer::Dom();
+        s = new IPointer::Sam();
+        b = new IPointer::Bob();
+        c = &d->c;
+        a = &c->a;
+        b->s_ptr = a->s_ptr = s;
+        b->a_ptr = a;
+        c->b_ptr = b;
+        s->data = 500;
+
+        someInt = 500;
+        for(size_t i = 0; i < 5; i++) someIntArray[i] = 500;
+
+        for(size_t i = 0; i < 5; i++) a_array[i] = *a;
+
+        for(size_t i = 0; i < 5; i++) p.ptr_array[i] = a;
+        p.array_ptr = &a_array;
+        p.int_ptr = &someInt;
+        p.int_array_ptr = &someIntArray;
+        for(size_t i = 0; i < 5; i++) p.int_ptr_array[i] = &someInt;
+        p.a_ptr_vec.resize(5);
+        for(size_t i = 0; i < 5; i++) p.a_ptr_vec[i] = a;
+        str = "meowmeowmeow";
+        p.str_ref = &str;
+        a_vec.resize(5);
+        for(size_t i = 0; i < 5; i++) a_vec[i].s_ptr = s;
+        p.a_vec_ptr = &a_vec;
+    }
+    ~PointerInterface() {
+        delete d; delete s; delete b;
+    }
+    Return<void> bar1(bar1_cb _cb) override {
+        _cb(*s, s);
+        return Void();
+    }
+    Return<void> bar2(bar2_cb _cb) override {
+        _cb(*s, *a);
+        return Void();
+    }
+    Return<void> bar3(bar3_cb _cb) override {
+        _cb(*s, *a, *b);
+        return Void();
+    }
+    Return<void> bar4(bar4_cb _cb) override {
+        _cb(s);
+        return Void();
+    }
+    Return<void> bar5(bar5_cb _cb) override {
+        _cb(*a, *b);
+        return Void();
+    }
+    Return<void> bar6(bar6_cb _cb) override {
+        _cb(a);
+        return Void();
+    }
+    Return<void> bar7(bar7_cb _cb) override {
+        _cb(a, b);
+        return Void();
+    }
+    Return<void> bar8(bar8_cb _cb) override {
+        _cb(*d);
+        return Void();
+    }
+    Return<void> bar9(bar9_cb _cb) override {
+        _cb(&str);
+        return Void();
+    }
+    Return<void> bar10(bar10_cb _cb) override {
+        hidl_vec<const IPointer::Sam *> v; v.resize(5);
+        for(size_t i = 0; i < 5; i++) v[i] = s;
+        _cb(v);
+        return Void();
+    }
+    Return<void> bar11(bar11_cb _cb) override {
+        hidl_vec<IPointer::Sam> v; v.resize(5);
+        for(size_t i = 0; i < 5; i++) v[i].data = 500;
+            _cb(&v);
+        return Void();
+    }
+    Return<void> bar12(bar12_cb _cb) override {
+        hidl_array<IPointer::Sam, 5> array;
+        for(size_t i = 0; i < 5; i++) array[i] = *s;
+        _cb(&array);
+        return Void();
+    }
+    Return<void> bar13(bar13_cb _cb) override {
+        hidl_array<const IPointer::Sam *, 5> array;
+        for(size_t i = 0; i < 5; i++) array[i] = s;
+        _cb(array);
+        return Void();
+    }
+    Return<void> bar14(bar14_cb _cb) override {
+        IPointer::Sam const* p1 = s;
+        IPointer::Sam const* const* p2 = &p1;
+        _cb(&p2);
+        return Void();
+    }
+    Return<void> bar15(bar15_cb _cb) override {
+        int32_t const* p1 = &someInt;
+        int32_t const* const* p2 = &p1;
+        _cb(&p2);
+        return Void();
+    }
+    Return<void> bar16(bar16_cb _cb) override {
+        _cb(p);
+        return Void();
+    }
+    Return<void> bar17(bar17_cb _cb) override {
+        _cb(&p);
+        return Void();
+    }
+    Return<void> bar18(bar18_cb _cb) override {
+        _cb(&str, &str, str);
+        return Void();
+    }
+    Return<void> bar19(bar19_cb _cb) override {
+        _cb(&a_vec, a_vec, &a_vec);
+        return Void();
+    }
+    Return<void> bar21(bar21_cb _cb) override {
+        hidl_array<IPointer::Ada, 3, 2, 1> a_array;
+        for(size_t i = 0; i < 3; i++)
+            for(size_t j = 0; j < 2; j++)
+                for(size_t k = 0; k < 1; k++)
+                    a_array[i][j][k] = *a;
+        _cb(&a_array);
+        return Void();
+    }
+    Return<void> bar22(bar22_cb _cb) override {
+        hidl_array<const IPointer::Ada *, 3, 2, 1> a_ptr_array;
+        for(size_t i = 0; i < 3; i++)
+            for(size_t j = 0; j < 2; j++)
+                for(size_t k = 0; k < 1; k++)
+                    a_ptr_array[i][j][k] = a;
+        _cb(a_ptr_array);
+        return Void();
+    }
+};
+
 struct FooCallback : public IFooCallback {
     FooCallback() : mLock{}, mCond{} {}
     Return<void> heyItsYou(const sp<IFooCallback> &cb) override;
@@ -549,17 +980,6 @@
     return Void();
 }
 
-#define EXPECT_OK(ret) EXPECT_TRUE(ret.getStatus().isOk())
-
-template<typename T, typename S>
-static inline bool isArrayEqual(const T arr1, const S arr2, size_t size) {
-    for(size_t i = 0; i < size; i++)
-        if(arr1[i] != arr2[i])
-            return false;
-    return true;
-}
-
-
 template <class T>
 static void startServer(T server,
                         const char *serviceName,
@@ -574,14 +994,27 @@
     ALOGI("SERVER(%s) ends.", tag);
 }
 
+static void killServer(pid_t pid, const char *serverName) {
+    if(kill(pid, SIGTERM)) {
+        ALOGE("Could not kill %s; errno = %d", serverName, errno);
+    } else {
+        int status;
+        ALOGI("Waiting for %s to exit...", serverName);
+        waitpid(pid, &status, 0);
+        ALOGI("Continuing...");
+    }
+}
+
 
 class HidlTest : public ::testing::Test {
 public:
-    sp<::android::hardware::IBinder> service;
     sp<IFoo> foo;
     sp<IBar> bar;
     sp<IFooCallback> fooCb;
-    sp<::android::hardware::IBinder> cbService;
+    sp<IGraph> graphInterface;
+    sp<IPointer> pointerInterface;
+    PointerInterface validationPointerInterface;
+
     virtual void SetUp() override {
         ALOGI("Test setup beginning...");
         using namespace android::hardware;
@@ -594,6 +1027,12 @@
         fooCb = IFooCallback::getService("foo callback");
         CHECK(fooCb != NULL);
 
+        graphInterface = IGraph::getService("graph");
+        CHECK(graphInterface != NULL);
+
+        pointerInterface = IPointer::getService("pointer");
+        CHECK(graphInterface != NULL);
+
         ALOGI("Test setup complete");
     }
     virtual void TearDown() override {
@@ -602,7 +1041,7 @@
 
 class HidlEnvironment : public ::testing::Environment {
 private:
-    pid_t fooCallbackServerPid, barServerPid;
+    pid_t fooCallbackServerPid, barServerPid, graphServerPid, pointerServerPid;
 public:
     virtual void SetUp() {
         ALOGI("Environment setup beginning...");
@@ -619,6 +1058,18 @@
             return;
         }
 
+        if ((graphServerPid = fork()) == 0) {
+            // Fear me, I am a third child.
+            startServer(new GraphInterface, "graph", "Graph"); // never returns
+            return;
+        }
+
+        if ((pointerServerPid = fork()) == 0) {
+            // Fear me, I am a forth child.
+            startServer(new PointerInterface, "pointer", "Pointer"); // never returns
+            return;
+        }
+
         // Fear you not, I am parent.
         sleep(1);
         ALOGI("Environment setup complete.");
@@ -628,22 +1079,10 @@
         // clean up by killing server processes.
         ALOGI("Environment tear-down beginning...");
         ALOGI("Killing servers...");
-        if(kill(barServerPid, SIGTERM)) {
-            ALOGE("Could not kill barServer; errno = %d", errno);
-        } else {
-            int status;
-            ALOGI("Waiting for barServer to exit...");
-            waitpid(barServerPid, &status, 0);
-            ALOGI("Continuing...");
-        }
-        if(kill(fooCallbackServerPid, SIGTERM)) {
-            ALOGE("Could not kill fooCallbackServer; errno = %d", errno);
-        } else {
-            int status;
-            ALOGI("Waiting for fooCallbackServer to exit...");
-            waitpid(fooCallbackServerPid, &status, 0);
-            ALOGI("Continuing...");
-        }
+        killServer(barServerPid, "barServer");
+        killServer(fooCallbackServerPid, "fooCallbackServer");
+        killServer(graphServerPid, "graphServer");
+        killServer(pointerServerPid, "pointerServer");
         ALOGI("Servers all killed.");
         ALOGI("Environment tear-down complete.");
     }
@@ -1007,8 +1446,288 @@
     EXPECT_EQ(threeDim.size(), std::make_tuple(2u, 3u, 4u));
 }
 
-int main(int argc, char **argv) {
+#if HIDL_RUN_POINTER_TESTS
 
+TEST_F(HidlTest, PassAGraphTest) {
+    IGraph::Graph g;
+    simpleGraph(g);
+    logSimpleGraph("CLIENT", g);
+    ALOGI("CLIENT call passAGraph");
+    EXPECT_OK(graphInterface->passAGraph(g));
+}
+
+TEST_F(HidlTest, GiveAGraphTest) {
+    EXPECT_OK(graphInterface->giveAGraph([&](const auto &newGraph) {
+        logSimpleGraph("CLIENT", newGraph);
+        EXPECT_TRUE(isSimpleGraph(newGraph));
+    }));
+}
+TEST_F(HidlTest, PassANodeTest) {
+    IGraph::Node node; node.data = 10;
+    EXPECT_OK(graphInterface->passANode(node));
+}
+TEST_F(HidlTest, PassTwoGraphsTest) {
+    IGraph::Graph g;
+    simpleGraph(g);
+    EXPECT_OK(graphInterface->passTwoGraphs(&g, &g));
+}
+TEST_F(HidlTest, PassAGammaTest) {
+    IGraph::Theta s; s.data = 500;
+    IGraph::Alpha a; a.s_ptr = &s;
+    IGraph::Beta  b; b.s_ptr = &s;
+    IGraph::Gamma c; c.a_ptr = &a; c.b_ptr = &b;
+    ALOGI("CLIENT calling passAGamma: c.a = %p, c.b = %p, c.a->s = %p, c.b->s = %p",
+        c.a_ptr, c.b_ptr, c.a_ptr->s_ptr, c.b_ptr->s_ptr);
+    EXPECT_OK(graphInterface->passAGamma(c));
+}
+TEST_F(HidlTest, PassNullTest) {
+    IGraph::Gamma c;
+    c.a_ptr = nullptr;
+    c.b_ptr = nullptr;
+    EXPECT_OK(graphInterface->passAGamma(c));
+}
+TEST_F(HidlTest, PassASimpleRefTest) {
+    IGraph::Theta s;
+    s.data = 500;
+    IGraph::Alpha a;
+    a.s_ptr = &s;
+    EXPECT_OK(graphInterface->passASimpleRef(&a));
+}
+TEST_F(HidlTest, PassASimpleRefSTest) {
+    IGraph::Theta s;
+    s.data = 500;
+    ALOGI("CLIENT call passASimpleRefS with %p", &s);
+    EXPECT_OK(graphInterface->passASimpleRefS(&s));
+}
+TEST_F(HidlTest, GiveASimpleRefTest) {
+    EXPECT_OK(graphInterface->giveASimpleRef([&](const auto & a_ptr) {
+        EXPECT_EQ(a_ptr->s_ptr->data, 500);
+    }));
+}
+TEST_F(HidlTest, GraphReportErrorsTest) {
+    Return<int32_t> ret = graphInterface->getErrors();
+    EXPECT_OK(ret);
+    EXPECT_EQ(int32_t(ret), 0);
+}
+
+TEST_F(HidlTest, PointerPassOldBufferTest) {
+    EXPECT_OK(validationPointerInterface.bar1([&](const auto& sptr, const auto& s) {
+        EXPECT_OK(pointerInterface->foo1(sptr, s));
+    }));
+}
+TEST_F(HidlTest, PointerPassOldBufferTest2) {
+    EXPECT_OK(validationPointerInterface.bar2([&](const auto& s, const auto& a) {
+        EXPECT_OK(pointerInterface->foo2(s, a));
+    }));
+}
+TEST_F(HidlTest, PointerPassSameOldBufferPointerTest) {
+    EXPECT_OK(validationPointerInterface.bar3([&](const auto& s, const auto& a, const auto& b) {
+        EXPECT_OK(pointerInterface->foo3(s, a, b));
+    }));
+}
+TEST_F(HidlTest, PointerPassOnlyTest) {
+    EXPECT_OK(validationPointerInterface.bar4([&](const auto& s) {
+        EXPECT_OK(pointerInterface->foo4(s));
+    }));
+}
+TEST_F(HidlTest, PointerPassTwoEmbeddedTest) {
+    EXPECT_OK(validationPointerInterface.bar5([&](const auto& a, const auto& b) {
+        EXPECT_OK(pointerInterface->foo5(a, b));
+    }));
+}
+TEST_F(HidlTest, PointerPassIndirectBufferHasDataTest) {
+    EXPECT_OK(validationPointerInterface.bar6([&](const auto& a) {
+        EXPECT_OK(pointerInterface->foo6(a));
+    }));
+}
+TEST_F(HidlTest, PointerPassTwoIndirectBufferTest) {
+    EXPECT_OK(validationPointerInterface.bar7([&](const auto& a, const auto& b) {
+        EXPECT_OK(pointerInterface->foo7(a, b));
+    }));
+}
+TEST_F(HidlTest, PointerPassDeeplyIndirectTest) {
+    EXPECT_OK(validationPointerInterface.bar8([&](const auto& d) {
+        EXPECT_OK(pointerInterface->foo8(d));
+    }));
+}
+TEST_F(HidlTest, PointerPassStringRefTest) {
+    EXPECT_OK(validationPointerInterface.bar9([&](const auto& str) {
+        EXPECT_OK(pointerInterface->foo9(str));
+    }));
+}
+TEST_F(HidlTest, PointerPassRefVecTest) {
+    EXPECT_OK(validationPointerInterface.bar10([&](const auto& v) {
+        EXPECT_OK(pointerInterface->foo10(v));
+    }));
+}
+TEST_F(HidlTest, PointerPassVecRefTest) {
+    EXPECT_OK(validationPointerInterface.bar11([&](const auto& v) {
+        EXPECT_OK(pointerInterface->foo11(v));
+    }));
+}
+TEST_F(HidlTest, PointerPassArrayRefTest) {
+    EXPECT_OK(validationPointerInterface.bar12([&](const auto& array) {
+        EXPECT_OK(pointerInterface->foo12(array));
+    }));
+}
+TEST_F(HidlTest, PointerPassRefArrayTest) {
+    EXPECT_OK(validationPointerInterface.bar13([&](const auto& array) {
+        EXPECT_OK(pointerInterface->foo13(array));
+    }));
+}
+TEST_F(HidlTest, PointerPass3RefTest) {
+    EXPECT_OK(validationPointerInterface.bar14([&](const auto& p3) {
+        EXPECT_OK(pointerInterface->foo14(p3));
+    }));
+}
+TEST_F(HidlTest, PointerPassInt3RefTest) {
+    EXPECT_OK(validationPointerInterface.bar15([&](const auto& p3) {
+        EXPECT_OK(pointerInterface->foo15(p3));
+    }));
+}
+TEST_F(HidlTest, PointerPassEmbeddedPointersTest) {
+    EXPECT_OK(validationPointerInterface.bar16([&](const auto& p) {
+        EXPECT_OK(pointerInterface->foo16(p));
+    }));
+}
+TEST_F(HidlTest, PointerPassEmbeddedPointers2Test) {
+    EXPECT_OK(validationPointerInterface.bar17([&](const auto& p) {
+        EXPECT_OK(pointerInterface->foo17(p));
+    }));
+}
+TEST_F(HidlTest, PointerPassCopiedStringTest) {
+    EXPECT_OK(validationPointerInterface.bar18([&](const auto& str_ref, const auto& str_ref2, const auto& str) {
+        EXPECT_OK(pointerInterface->foo18(str_ref, str_ref2, str));
+    }));
+}
+TEST_F(HidlTest, PointerPassCopiedVecTest) {
+    EXPECT_OK(validationPointerInterface.bar19([&](const auto& a_vec_ref, const auto& a_vec, const auto& a_vec_ref2) {
+        EXPECT_OK(pointerInterface->foo19(a_vec_ref, a_vec, a_vec_ref2));
+    }));
+}
+TEST_F(HidlTest, PointerPassMultidimArrayRefTest) {
+    EXPECT_OK(validationPointerInterface.bar21([&](const auto& v) {
+        EXPECT_OK(pointerInterface->foo21(v));
+    }));
+}
+TEST_F(HidlTest, PointerPassRefMultidimArrayTest) {
+    EXPECT_OK(validationPointerInterface.bar22([&](const auto& v) {
+        EXPECT_OK(pointerInterface->foo22(v));
+    }));
+}
+TEST_F(HidlTest, PointerGiveOldBufferTest) {
+    EXPECT_OK(pointerInterface->bar1([&](const auto& sptr, const auto& s) {
+        EXPECT_OK(validationPointerInterface.foo1(sptr, s));
+    }));
+}
+TEST_F(HidlTest, PointerGiveOldBufferTest2) {
+    EXPECT_OK(pointerInterface->bar2([&](const auto& s, const auto& a) {
+        EXPECT_OK(validationPointerInterface.foo2(s, a));
+    }));
+}
+TEST_F(HidlTest, PointerGiveSameOldBufferPointerTest) {
+    EXPECT_OK(pointerInterface->bar3([&](const auto& s, const auto& a, const auto& b) {
+        EXPECT_OK(validationPointerInterface.foo3(s, a, b));
+    }));
+}
+TEST_F(HidlTest, PointerGiveOnlyTest) {
+    EXPECT_OK(pointerInterface->bar4([&](const auto& s) {
+        EXPECT_OK(validationPointerInterface.foo4(s));
+    }));
+}
+TEST_F(HidlTest, PointerGiveTwoEmbeddedTest) {
+    EXPECT_OK(pointerInterface->bar5([&](const auto& a, const auto& b) {
+        EXPECT_OK(validationPointerInterface.foo5(a, b));
+    }));
+}
+TEST_F(HidlTest, PointerGiveIndirectBufferHasDataTest) {
+    EXPECT_OK(pointerInterface->bar6([&](const auto& a) {
+        EXPECT_OK(validationPointerInterface.foo6(a));
+    }));
+}
+TEST_F(HidlTest, PointerGiveTwoIndirectBufferTest) {
+    EXPECT_OK(pointerInterface->bar7([&](const auto& a, const auto& b) {
+        EXPECT_OK(validationPointerInterface.foo7(a, b));
+    }));
+}
+TEST_F(HidlTest, PointerGiveDeeplyIndirectTest) {
+    EXPECT_OK(pointerInterface->bar8([&](const auto& d) {
+        EXPECT_OK(validationPointerInterface.foo8(d));
+    }));
+}
+TEST_F(HidlTest, PointerGiveStringRefTest) {
+    EXPECT_OK(pointerInterface->bar9([&](const auto& str) {
+        EXPECT_OK(validationPointerInterface.foo9(str));
+    }));
+}
+TEST_F(HidlTest, PointerGiveRefVecTest) {
+    EXPECT_OK(pointerInterface->bar10([&](const auto& v) {
+        EXPECT_OK(validationPointerInterface.foo10(v));
+    }));
+}
+TEST_F(HidlTest, PointerGiveVecRefTest) {
+    EXPECT_OK(pointerInterface->bar11([&](const auto& v) {
+        EXPECT_OK(validationPointerInterface.foo11(v));
+    }));
+}
+TEST_F(HidlTest, PointerGiveArrayRefTest) {
+    EXPECT_OK(pointerInterface->bar12([&](const auto& array) {
+        EXPECT_OK(validationPointerInterface.foo12(array));
+    }));
+}
+TEST_F(HidlTest, PointerGiveRefArrayTest) {
+    EXPECT_OK(pointerInterface->bar13([&](const auto& array) {
+        EXPECT_OK(validationPointerInterface.foo13(array));
+    }));
+}
+TEST_F(HidlTest, PointerGive3RefTest) {
+    EXPECT_OK(pointerInterface->bar14([&](const auto& p3) {
+        EXPECT_OK(validationPointerInterface.foo14(p3));
+    }));
+}
+TEST_F(HidlTest, PointerGiveInt3RefTest) {
+    EXPECT_OK(pointerInterface->bar15([&](const auto& p3) {
+        EXPECT_OK(validationPointerInterface.foo15(p3));
+    }));
+}
+TEST_F(HidlTest, PointerGiveEmbeddedPointersTest) {
+    EXPECT_OK(pointerInterface->bar16([&](const auto& p) {
+        EXPECT_OK(validationPointerInterface.foo16(p));
+    }));
+}
+TEST_F(HidlTest, PointerGiveEmbeddedPointers2Test) {
+    EXPECT_OK(pointerInterface->bar17([&](const auto& p) {
+        EXPECT_OK(validationPointerInterface.foo17(p));
+    }));
+}
+TEST_F(HidlTest, PointerGiveCopiedStringTest) {
+    EXPECT_OK(pointerInterface->bar18([&](const auto& str_ref, const auto& str_ref2, const auto& str) {
+        EXPECT_OK(validationPointerInterface.foo18(str_ref, str_ref2, str));
+    }));
+}
+TEST_F(HidlTest, PointerGiveCopiedVecTest) {
+    EXPECT_OK(pointerInterface->bar19([&](const auto& a_vec_ref, const auto& a_vec, const auto& a_vec_ref2) {
+        EXPECT_OK(validationPointerInterface.foo19(a_vec_ref, a_vec, a_vec_ref2));
+    }));
+}
+TEST_F(HidlTest, PointerGiveMultidimArrayRefTest) {
+    EXPECT_OK(pointerInterface->bar21([&](const auto& v) {
+        EXPECT_OK(validationPointerInterface.foo21(v));
+    }));
+}
+TEST_F(HidlTest, PointerGiveRefMultidimArrayTest) {
+    EXPECT_OK(pointerInterface->bar22([&](const auto& v) {
+        EXPECT_OK(validationPointerInterface.foo22(v));
+    }));
+}
+TEST_F(HidlTest, PointerReportErrorsTest) {
+    Return<int32_t> ret = pointerInterface->getErrors();
+    EXPECT_OK(ret);
+    EXPECT_EQ(int32_t(ret), 0);
+}
+#endif
+
+int main(int argc, char **argv) {
     ::testing::AddGlobalTestEnvironment(new HidlEnvironment);
     ::testing::InitGoogleTest(&argc, argv);
     int status = RUN_ALL_TESTS();