Merge "Add compile time test to c2hal"
diff --git a/AST.h b/AST.h
index f82a9ba..b4270f8 100644
--- a/AST.h
+++ b/AST.h
@@ -161,41 +161,26 @@
     status_t generateTypeSource(
             Formatter &out, const std::string &ifaceName) const;
 
-    enum MethodLocation {
-        PROXY_HEADER,
-        STUB_HEADER,
-        IMPL_HEADER,
-        IMPL_SOURCE,
-        PASSTHROUGH_HEADER
-    };
+    // a method, and in which interface is it originally defined.
+    // be careful of the case where method.isHidlReserved(), where interface
+    // is effectively useless.
+    using MethodGenerator = std::function<status_t(const Method *, const Interface *)>;
 
     status_t generateStubImplHeader(const std::string &outputPath) const;
     status_t generateStubImplSource(const std::string &outputPath) const;
 
-    status_t generateMethods(Formatter &out,
-                             const std::string &className,
-                             MethodLocation type,
-                             bool specifyNamespaces) const;
+    status_t generateMethods(Formatter &out, MethodGenerator gen) const;
     status_t generateStubMethod(Formatter &out,
-                                const std::string &className,
-                                const Method *method,
-                                bool specifyNamespaces) const;
-    status_t generateProxyDeclaration(Formatter &out,
-                                      const std::string &className,
-                                      const Method *method,
-                                      bool specifyNamespaces) const;
-    status_t generateStubImplDeclaration(Formatter &out,
-                                         const std::string &className,
-                                         const Method *method,
-                                         bool specifyNamespaces) const;
+                                const Method *method) const;
     status_t generateStubImplMethod(Formatter &out,
                                     const std::string &className,
-                                    const Method *method,
-                                    bool specifyNamespaces) const;
+                                    const Method *method) const;
     status_t generatePassthroughMethod(Formatter &out,
+                                       const Method *method) const;
+    status_t generateProxyMethodSource(Formatter &out,
                                        const std::string &className,
                                        const Method *method,
-                                       bool specifyNamespaces) const;
+                                       const Interface *superInterface) const;
 
     void generateFetchSymbol(Formatter &out, const std::string &ifaceName) const;
 
diff --git a/CompoundType.cpp b/CompoundType.cpp
index eb5d083..870402b 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -758,7 +758,7 @@
 }
 
 status_t CompoundType::emitVtsTypeDeclarations(Formatter &out) const {
-    out << "name: \"" << localName() << "\"\n";
+    out << "name: \"" << fullName() << "\"\n";
     out << "type: " << getVtsType() << "\n";
 
     // Emit declaration for each subtype.
@@ -813,7 +813,7 @@
 
 status_t CompoundType::emitVtsAttributeType(Formatter &out) const {
     out << "type: " << getVtsType() << "\n";
-    out << "predefined_type: \"" << localName() << "\"\n";
+    out << "predefined_type: \"" << fullName() << "\"\n";
     return OK;
 }
 
diff --git a/EnumType.cpp b/EnumType.cpp
index 1905a3e..27c0075 100644
--- a/EnumType.cpp
+++ b/EnumType.cpp
@@ -295,7 +295,7 @@
 }
 
 status_t EnumType::emitVtsTypeDeclarations(Formatter &out) const {
-    out << "name: \"" << localName() << "\"\n";
+    out << "name: \"" << fullName() << "\"\n";
     out << "type: " << getVtsType() << "\n";
     out << "enum_value: {\n";
     out.indent();
@@ -334,7 +334,7 @@
 
 status_t EnumType::emitVtsAttributeType(Formatter &out) const {
     out << "type: " << getVtsType() << "\n";
-    out << "predefined_type: \"" << localName() << "\"\n";
+    out << "predefined_type: \"" << fullName() << "\"\n";
     return OK;
 }
 
diff --git a/Interface.cpp b/Interface.cpp
index d348101..f03f3aa 100644
--- a/Interface.cpp
+++ b/Interface.cpp
@@ -252,11 +252,21 @@
         out.unindent();
         out << "}\n\n";
     } else {
+        out << "if (" << name << " == nullptr) {\n";
+        out.indent();
+        out << "_hidl_err = ";
+        out << parcelObjDeref
+            << "writeStrongBinder(nullptr);\n";
+        out.unindent();
+        out << "} else {\n";
+        out.indent();
         out << "_hidl_err = "
             << parcelObjDeref
             << "writeStrongBinder("
             << name
             << "->toBinder());\n";
+        out.unindent();
+        out << "}\n";
 
         handleError(out, mode);
     }
diff --git a/Method.h b/Method.h
index 7e711f0..4f5163d 100644
--- a/Method.h
+++ b/Method.h
@@ -62,8 +62,8 @@
     size_t getSerialId() const;
 
     void generateCppSignature(Formatter &out,
-                              const std::string &className,
-                              bool specifyNamespaces) const;
+                              const std::string &className = "",
+                              bool specifyNamespaces = true) const;
 
     static std::string GetArgSignature(const std::vector<TypedVar *> &args,
                                        bool specifyNamespaces);
diff --git a/generateCpp.cpp b/generateCpp.cpp
index 69ded55..ffa6711 100644
--- a/generateCpp.cpp
+++ b/generateCpp.cpp
@@ -416,14 +416,10 @@
 }
 
 status_t AST::generateStubMethod(Formatter &out,
-                                 const std::string &className,
-                                 const Method *method,
-                                 bool specifyNamespaces) const {
+                                 const Method *method) const {
     out << "inline ";
 
-    method->generateCppSignature(out,
-                                 className,
-                                 specifyNamespaces);
+    method->generateCppSignature(out);
 
     const bool returnsValue = !method->results().empty();
     const TypedVar *elidedReturn = method->canElideCallback();
@@ -456,25 +452,9 @@
     return OK;
 }
 
-status_t AST::generateProxyDeclaration(Formatter &out,
-                                       const std::string &className,
-                                       const Method *method,
-                                       bool specifyNamespaces) const {
-
-    method->generateCppSignature(out,
-                                 className,
-                                 specifyNamespaces);
-    out << " override;\n";
-
-    return OK;
-}
-
-
 status_t AST::generatePassthroughMethod(Formatter &out,
-                                        const std::string &className,
-                                        const Method *method,
-                                        bool specifyNamespaces) const {
-    method->generateCppSignature(out, className, specifyNamespaces);
+                                        const Method *method) const {
+    method->generateCppSignature(out);
 
     out << " {\n";
     out.indent();
@@ -528,11 +508,7 @@
     return OK;
 }
 
-status_t AST::generateMethods(
-        Formatter &out,
-        const std::string &className,
-        MethodLocation type,
-        bool specifyNamespaces) const {
+status_t AST::generateMethods(Formatter &out, MethodGenerator gen) const {
 
     const Interface *iface = mRootScope->getInterface();
 
@@ -550,42 +526,7 @@
                 << " follow.\n";
             prevIterface = superInterface;
         }
-        status_t err;
-        switch(type) {
-            case STUB_HEADER:
-                err = generateStubMethod(out,
-                                         className,
-                                         method,
-                                         specifyNamespaces);
-                break;
-            case PROXY_HEADER:
-                err = generateProxyDeclaration(out,
-                                               className,
-                                               method,
-                                               specifyNamespaces);
-                break;
-            case IMPL_HEADER:
-                err = generateStubImplDeclaration(out,
-                                                  className,
-                                                  method,
-                                                  specifyNamespaces);
-                break;
-            case IMPL_SOURCE:
-                err = generateStubImplMethod(out,
-                                             className,
-                                             method,
-                                             specifyNamespaces);
-                break;
-            case PASSTHROUGH_HEADER:
-                err = generatePassthroughMethod(out,
-                                                className,
-                                                method,
-                                                specifyNamespaces);
-                break;
-            default:
-                LOG(ERROR) << "Unkown method type: " << type;
-                err = UNKNOWN_ERROR;
-        }
+        status_t err = gen(method, superInterface);
 
         if (err != OK) {
             return err;
@@ -664,10 +605,9 @@
     out.unindent();
     out.unindent();
 
-    status_t err = generateMethods(out,
-                                   "" /* class name */,
-                                   MethodLocation::STUB_HEADER,
-                                   true /* specify namespaces */);
+    status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
+        return generateStubMethod(out, method);
+    });
 
     if (err != OK) {
         return err;
@@ -744,10 +684,11 @@
 
     out << "virtual bool isRemote() const override { return true; }\n\n";
 
-    status_t err = generateMethods(out,
-                                   "" /* class name */,
-                                   MethodLocation::PROXY_HEADER,
-                                   true /* generate specify namespaces */);
+    status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
+        method->generateCppSignature(out);
+        out << " override;\n";
+        return OK;
+    });
 
     if (err != OK) {
         return err;
@@ -959,6 +900,180 @@
     }
 }
 
+status_t AST::generateProxyMethodSource(Formatter &out,
+                                        const std::string &klassName,
+                                        const Method *method,
+                                        const Interface *superInterface) const {
+
+    method->generateCppSignature(out,
+                                 klassName,
+                                 true /* specify namespaces */);
+
+    const bool returnsValue = !method->results().empty();
+    const TypedVar *elidedReturn = method->canElideCallback();
+
+    out << "{\n";
+
+    out.indent();
+
+    if (returnsValue && elidedReturn == nullptr) {
+        generateCheckNonNull(out, "_hidl_cb");
+    }
+
+    status_t status = generateCppInstrumentationCall(
+            out,
+            InstrumentationEvent::CLIENT_API_ENTRY,
+            superInterface,
+            method);
+    if (status != OK) {
+        return status;
+    }
+
+    out << "::android::hardware::Parcel _hidl_data;\n";
+    out << "::android::hardware::Parcel _hidl_reply;\n";
+    out << "::android::status_t _hidl_err;\n";
+    out << "::android::hardware::Status _hidl_status;\n\n";
+
+    declareCppReaderLocals(
+            out, method->results(), true /* forResults */);
+
+    out << "_hidl_err = _hidl_data.writeInterfaceToken(";
+    if (!method->isHidlReserved()) {
+        out << superInterface->fqName().cppNamespace()
+            << "::IHw"
+            << superInterface->getBaseName();
+    } else {
+        out << "::android::hardware::IHidlInterfaceBase";
+    }
+    out << "::descriptor);\n";
+
+    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,
+                "_hidl_data",
+                false /* parcelObjIsPointer */,
+                arg,
+                false /* reader */,
+                Type::ErrorMode_Goto,
+                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("
+        << method->getSerialId()
+        << " /* "
+        << method->name()
+        << " */, _hidl_data, &_hidl_reply";
+
+    if (method->isOneway()) {
+        out << ", ::android::hardware::IBinder::FLAG_ONEWAY";
+    }
+    out << ");\n";
+
+    out << "if (_hidl_err != ::android::OK) { goto _hidl_error; }\n\n";
+
+    if (!method->isOneway()) {
+        out << "_hidl_err = _hidl_status.readFromParcel(_hidl_reply);\n";
+        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,
+                    "_hidl_reply",
+                    false /* parcelObjIsPointer */,
+                    arg,
+                    true /* reader */,
+                    Type::ErrorMode_Goto,
+                    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 << "_hidl_cb(";
+
+            bool first = true;
+            for (const auto &arg : method->results()) {
+                if (!first) {
+                    out << ", ";
+                }
+
+                if (arg->type().resultNeedsDeref()) {
+                    out << "*";
+                }
+                out << "_hidl_out_" << arg->name();
+
+                first = false;
+            }
+
+            out << ");\n\n";
+        }
+        status_t status = generateCppInstrumentationCall(
+                out,
+                InstrumentationEvent::CLIENT_API_EXIT,
+                superInterface,
+                method);
+        if (status != OK) {
+            return status;
+        }
+    }
+
+    if (elidedReturn != nullptr) {
+        std::string extra;
+        out << "_hidl_status.setFromStatusT(_hidl_err);\n";
+        out << "return ::android::hardware::Return<";
+        out << elidedReturn->type().getCppResultType(&extra)
+            << ">(_hidl_out_" << elidedReturn->name() << ");\n\n";
+    } else {
+        out << "_hidl_status.setFromStatusT(_hidl_err);\n";
+        out << "return ::android::hardware::Return<void>();\n\n";
+    }
+
+    out.unindent();
+    out << "_hidl_error:\n";
+    out.indent();
+    out << "_hidl_status.setFromStatusT(_hidl_err);\n";
+    out << "return ::android::hardware::Return<";
+    if (elidedReturn != nullptr) {
+        std::string extra;
+        out << method->results().at(0)->type().getCppResultType(&extra);
+    } else {
+        out << "void";
+    }
+    out << ">(_hidl_status);\n";
+
+    out.unindent();
+    out << "}\n\n";
+    return OK;
+}
+
 status_t AST::generateProxySource(
         Formatter &out, const std::string &baseName) const {
     const std::string klassName = "Bp" + baseName;
@@ -985,181 +1100,11 @@
     out.unindent();
     out << "}\n\n";
 
-    const Interface *iface = mRootScope->getInterface();
+    status_t err = generateMethods(out, [&](const Method *method, const Interface *superInterface) {
+        return generateProxyMethodSource(out, klassName, method, superInterface);
+    });
 
-    for (const auto &tuple : iface->allMethodsFromRoot()) {
-        const Method *method = tuple.method();
-        const Interface *superInterface = tuple.interface();
-        method->generateCppSignature(out,
-                                     klassName,
-                                     true /* specify namespaces */);
-
-        const bool returnsValue = !method->results().empty();
-        const TypedVar *elidedReturn = method->canElideCallback();
-
-        out << "{\n";
-
-        out.indent();
-
-        if (returnsValue && elidedReturn == nullptr) {
-            generateCheckNonNull(out, "_hidl_cb");
-        }
-
-        status_t status = generateCppInstrumentationCall(
-                out,
-                InstrumentationEvent::CLIENT_API_ENTRY,
-                superInterface,
-                method);
-        if (status != OK) {
-            return status;
-        }
-
-        out << "::android::hardware::Parcel _hidl_data;\n";
-        out << "::android::hardware::Parcel _hidl_reply;\n";
-        out << "::android::status_t _hidl_err;\n";
-        out << "::android::hardware::Status _hidl_status;\n\n";
-
-        declareCppReaderLocals(
-                out, method->results(), true /* forResults */);
-
-        out << "_hidl_err = _hidl_data.writeInterfaceToken(";
-        if (!method->isHidlReserved()) {
-            out << superInterface->fqName().cppNamespace()
-                << "::IHw"
-                << superInterface->getBaseName();
-        } else {
-            out << "::android::hardware::IHidlInterfaceBase";
-        }
-        out << "::descriptor);\n";
-
-        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,
-                    "_hidl_data",
-                    false /* parcelObjIsPointer */,
-                    arg,
-                    false /* reader */,
-                    Type::ErrorMode_Goto,
-                    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("
-            << method->getSerialId()
-            << " /* "
-            << method->name()
-            << " */, _hidl_data, &_hidl_reply";
-
-        if (method->isOneway()) {
-            out << ", ::android::hardware::IBinder::FLAG_ONEWAY";
-        }
-        out << ");\n";
-
-        out << "if (_hidl_err != ::android::OK) { goto _hidl_error; }\n\n";
-
-        if (!method->isOneway()) {
-            out << "_hidl_err = _hidl_status.readFromParcel(_hidl_reply);\n";
-            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,
-                        "_hidl_reply",
-                        false /* parcelObjIsPointer */,
-                        arg,
-                        true /* reader */,
-                        Type::ErrorMode_Goto,
-                        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 << "_hidl_cb(";
-
-                bool first = true;
-                for (const auto &arg : method->results()) {
-                    if (!first) {
-                        out << ", ";
-                    }
-
-                    if (arg->type().resultNeedsDeref()) {
-                        out << "*";
-                    }
-                    out << "_hidl_out_" << arg->name();
-
-                    first = false;
-                }
-
-                out << ");\n\n";
-            }
-            status_t status = generateCppInstrumentationCall(
-                    out,
-                    InstrumentationEvent::CLIENT_API_EXIT,
-                    superInterface,
-                    method);
-            if (status != OK) {
-                return status;
-            }
-        }
-
-        if (elidedReturn != nullptr) {
-            std::string extra;
-            out << "_hidl_status.setFromStatusT(_hidl_err);\n";
-            out << "return ::android::hardware::Return<";
-            out << elidedReturn->type().getCppResultType(&extra)
-                << ">(_hidl_out_" << elidedReturn->name() << ");\n\n";
-        } else {
-            out << "_hidl_status.setFromStatusT(_hidl_err);\n";
-            out << "return ::android::hardware::Return<void>();\n\n";
-        }
-
-        out.unindent();
-        out << "_hidl_error:\n";
-        out.indent();
-        out << "_hidl_status.setFromStatusT(_hidl_err);\n";
-        out << "return ::android::hardware::Return<";
-        if (elidedReturn != nullptr) {
-            std::string extra;
-            out << method->results().at(0)->type().getCppResultType(&extra);
-        } else {
-            out << "void";
-        }
-        out << ">(_hidl_status);\n";
-
-        out.unindent();
-        out << "}\n\n";
-    }
-
-
-    return OK;
+    return err;
 }
 
 status_t AST::generateStubSource(
@@ -1575,10 +1520,9 @@
         << ifaceName
         << "> impl);\n";
 
-    status_t err = generateMethods(out,
-                                   "" /* class name */,
-                                   MethodLocation::PASSTHROUGH_HEADER,
-                                   true /* specify namespaces */);
+    status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
+        return generatePassthroughMethod(out, method);
+    });
 
     if (err != OK) {
         return err;
diff --git a/generateCppImpl.cpp b/generateCppImpl.cpp
index 11ec1d6..570635f 100644
--- a/generateCppImpl.cpp
+++ b/generateCppImpl.cpp
@@ -48,15 +48,14 @@
 
 status_t AST::generateStubImplMethod(Formatter &out,
                                      const std::string &className,
-                                     const Method *method,
-                                     bool specifyNamespaces) const {
+                                     const Method *method) const {
 
     // ignore HIDL reserved methods -- implemented in IFoo already.
     if (method->isHidlReserved()) {
         return OK;
     }
 
-    method->generateCppSignature(out, className, specifyNamespaces);
+    method->generateCppSignature(out, className, false /* specifyNamespaces */);
 
     out << " {\n";
 
@@ -81,24 +80,6 @@
     return OK;
 }
 
-status_t AST::generateStubImplDeclaration(Formatter &out,
-                                          const std::string &className,
-                                          const Method *method,
-                                          bool specifyNamespaces) const {
-
-    // ignore HIDL reserved methods -- implemented in IFoo already.
-    if (method->isHidlReserved()) {
-        return OK;
-    }
-
-    method->generateCppSignature(out,
-                                 className,
-                                 specifyNamespaces);
-    out << " override;\n";
-
-    return OK;
-}
-
 status_t AST::generateStubImplHeader(const std::string &outputPath) const {
     std::string ifaceName;
     if (!AST::isInterface(&ifaceName)) {
@@ -192,10 +173,16 @@
 
     out.indent();
 
-    status_t err = generateMethods(out,
-                                   "", /* class name */
-                                   MethodLocation::IMPL_HEADER,
-                                   false /* specify namespaces */);
+    status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
+        // ignore HIDL reserved methods -- implemented in IFoo already.
+        if (method->isHidlReserved()) {
+            return OK;
+        }
+        method->generateCppSignature(out, "" /* className */,
+                false /* specifyNamespaces */);
+        out << " override;\n";
+        return OK;
+    });
 
     if (err != OK) {
         return err;
@@ -250,11 +237,9 @@
     // this is namespace aware code and doesn't require post-processing
     out.setNamespace("");
 
-    generateMethods(out,
-                    baseName,
-                    MethodLocation::IMPL_SOURCE,
-                    false /* specify namespaces */);
-
+    status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
+        return generateStubImplMethod(out, baseName, method);
+    });
 
     out << ifaceName
         << "* ";
diff --git a/test/data/android/hardware/nfc/1.0/NfcClientCallback.vts b/test/data/android/hardware/nfc/1.0/NfcClientCallback.vts
index e2a3e5b..e39ea7c 100644
--- a/test/data/android/hardware/nfc/1.0/NfcClientCallback.vts
+++ b/test/data/android/hardware/nfc/1.0/NfcClientCallback.vts
@@ -11,11 +11,11 @@
         name: "sendEvent"
         arg: {
             type: TYPE_ENUM
-            predefined_type: "NfcEvent"
+            predefined_type: "::android::hardware::nfc::V1_0::NfcEvent"
         }
         arg: {
             type: TYPE_ENUM
-            predefined_type: "NfcStatus"
+            predefined_type: "::android::hardware::nfc::V1_0::NfcStatus"
         }
     }
 
diff --git a/test/data/android/hardware/nfc/1.0/types.vts b/test/data/android/hardware/nfc/1.0/types.vts
index 8e4f93b..e43db1e 100644
--- a/test/data/android/hardware/nfc/1.0/types.vts
+++ b/test/data/android/hardware/nfc/1.0/types.vts
@@ -6,7 +6,7 @@
 
 
 attribute: {
-    name: "NfcEvent"
+    name: "::android::hardware::nfc::V1_0::NfcEvent"
     type: TYPE_ENUM
     enum_value: {
         scalar_type: "uint32_t"
@@ -43,7 +43,7 @@
 }
 
 attribute: {
-    name: "NfcStatus"
+    name: "::android::hardware::nfc::V1_0::NfcStatus"
     type: TYPE_ENUM
     enum_value: {
         scalar_type: "uint32_t"
diff --git a/test/main.cpp b/test/main.cpp
index 50bada6..8aa3da8 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -641,6 +641,14 @@
     }
 }
 
+TEST_F(HidlTest, FooNullCallbackTest) {
+    EXPECT_OK(foo->echoNullInterface(nullptr,
+                [](const auto receivedNull, const auto &intf) {
+                   EXPECT_TRUE(receivedNull);
+                   EXPECT_EQ(intf, nullptr);
+                }));
+}
+
 TEST_F(HidlTest, FooNonNullCallbackTest) {
     hidl_array<hidl_string, 5, 3> in;