Merge "Oneway keyword behavior in passthrough mode."
diff --git a/AST.h b/AST.h
index 59fb62c..2950a6f 100644
--- a/AST.h
+++ b/AST.h
@@ -140,6 +140,7 @@
     status_t generateStubHeader(const std::string &outputPath) const;
     status_t generateProxyHeader(const std::string &outputPath) const;
     status_t generateAllSource(const std::string &outputPath) const;
+    status_t generatePassthroughHeader(const std::string &outputPath) const;
 
     status_t generateTypeSource(
             Formatter &out, const std::string &ifaceName) const;
@@ -148,7 +149,8 @@
         PROXY_HEADER,
         STUB_HEADER,
         IMPL_HEADER,
-        IMPL_SOURCE
+        IMPL_SOURCE,
+        PASSTHROUGH_HEADER
     };
 
     status_t generateStubImplHeader(const std::string &outputPath) const;
@@ -174,6 +176,10 @@
                                     const std::string &className,
                                     const Method *method,
                                     bool specifyNamespaces) const;
+    status_t generatePassthroughMethod(Formatter &out,
+                                       const std::string &className,
+                                       const Method *method,
+                                       bool specifyNamespaces) const;
 
     void generateFetchSymbol(Formatter &out, const std::string &ifaceName) const;
 
@@ -186,6 +192,8 @@
     status_t generateStubSourceForMethod(
             Formatter &out, const Interface *iface, const Method *method) const;
 
+    status_t generatePassthroughSource(Formatter &out) const;
+
     void declareCppReaderLocals(
             Formatter &out, const std::vector<TypedVar *> &arg) const;
 
diff --git a/Interface.cpp b/Interface.cpp
index 10f6505..0fe7e87 100644
--- a/Interface.cpp
+++ b/Interface.cpp
@@ -260,6 +260,23 @@
     return OK;
 }
 
+
+bool Interface::hasOnewayMethods() const {
+    for (auto const &method : mMethods) {
+        if (method->isOneway()) {
+            return true;
+        }
+    }
+
+    const Interface* superClass = superType();
+
+    if (superClass != nullptr) {
+        return superClass->hasOnewayMethods();
+    }
+
+    return false;
+}
+
 bool Interface::isJavaCompatible() const {
     if (mIsJavaCompatibleInProgress) {
         // We're currently trying to determine if this Interface is
diff --git a/Interface.h b/Interface.h
index 4adfbae..0f12688 100644
--- a/Interface.h
+++ b/Interface.h
@@ -73,6 +73,8 @@
     status_t emitVtsAttributeDeclaration(Formatter &out) const;
     status_t emitVtsMethodDeclaration(Formatter &out) const;
 
+    bool hasOnewayMethods() const;
+
     bool isJavaCompatible() const override;
 
 private:
diff --git a/generateCpp.cpp b/generateCpp.cpp
index 3543e97..215d1eb 100644
--- a/generateCpp.cpp
+++ b/generateCpp.cpp
@@ -51,6 +51,10 @@
         err = generateAllSource(outputPath);
     }
 
+    if (err == OK) {
+        generatePassthroughHeader(outputPath);
+    }
+
     return err;
 }
 
@@ -461,6 +465,61 @@
     return OK;
 }
 
+
+status_t AST::generatePassthroughMethod(Formatter &out,
+                                        const std::string &className,
+                                        const Method *method,
+                                        bool specifyNamespaces) const {
+    method->generateCppSignature(out, className, specifyNamespaces);
+
+    out << " {\n";
+    out.indent();
+
+    const bool returnsValue = !method->results().empty();
+    const TypedVar *elidedReturn = method->canElideCallback();
+
+    out << "return ";
+
+    if (method->isOneway()) {
+        out << "addOnewayTask([this";
+        for (const auto &arg : method->args()) {
+            out << ", " << arg->name();
+        }
+        out << "] {this->";
+    }
+
+    out << "mImpl->"
+        << method->name()
+        << "(";
+
+    bool first = true;
+    for (const auto &arg : method->args()) {
+        if (!first) {
+            out << ", ";
+        }
+        first = false;
+        out << arg->name();
+    }
+    if (returnsValue && elidedReturn == nullptr) {
+        if (!method->args().empty()) {
+            out << ", ";
+        }
+
+        out << "_hidl_cb";
+    }
+    out << ")";
+
+    if (method->isOneway()) {
+        out << ";})";
+    }
+    out << ";\n";
+
+    out.unindent();
+    out << "}\n";
+
+    return OK;
+}
+
 status_t AST::generateMethods(
         Formatter &out,
         const std::string &className,
@@ -509,6 +568,12 @@
                                                  method,
                                                  specifyNamespaces);
                     break;
+                case PASSTHROUGH_HEADER:
+                    err = generatePassthroughMethod(out,
+                                                    className,
+                                                    method,
+                                                    specifyNamespaces);
+                    break;
                 default:
                     LOG(ERROR) << "Unkown method type: " << type;
                     err = UNKNOWN_ERROR;
@@ -739,6 +804,7 @@
     if (isInterface) {
         out << "#include <" << prefix << "/Bp" << baseName << ".h>\n";
         out << "#include <" << prefix << "/Bn" << baseName << ".h>\n";
+        out << "#include <" << prefix << "/Bs" << baseName << ".h>\n";
     } else {
         out << "#include <" << prefix << "types.h>\n";
     }
@@ -760,6 +826,10 @@
     }
 
     if (err == OK && isInterface) {
+        err = generatePassthroughSource(out);
+    }
+
+    if (err == OK && isInterface) {
         const Interface *iface = mRootScope->getInterface();
 
         out << "IMPLEMENT_REGISTER_AND_GET_SERVICE("
@@ -1255,5 +1325,156 @@
     return OK;
 }
 
+status_t AST::generatePassthroughHeader(const std::string &outputPath) const {
+    std::string ifaceName;
+    if (!AST::isInterface(&ifaceName)) {
+        // types.hal does not get a stub header.
+        return OK;
+    }
+
+    const Interface *iface = mRootScope->getInterface();
+
+    const std::string baseName = iface->getBaseName();
+    const std::string klassName = "Bs" + baseName;
+
+    bool supportOneway = iface->hasOnewayMethods();
+
+    std::string path = outputPath;
+    path.append(mCoordinator->convertPackageRootToPath(mPackage));
+    path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
+    path.append(klassName);
+    path.append(".h");
+
+    CHECK(Coordinator::MakeParentHierarchy(path));
+    FILE *file = fopen(path.c_str(), "w");
+
+    if (file == NULL) {
+        return -errno;
+    }
+
+    Formatter out(file);
+
+    const std::string guard = makeHeaderGuard(klassName);
+
+    out << "#ifndef " << guard << "\n";
+    out << "#define " << guard << "\n\n";
+
+    std::vector<std::string> packageComponents;
+    getPackageAndVersionComponents(
+            &packageComponents, false /* cpp_compatible */);
+
+    out << "#include <future>\n";
+    out << "#include <";
+    for (const auto &component : packageComponents) {
+        out << component << "/";
+    }
+    out << ifaceName << ".h>\n\n";
+
+    if (supportOneway) {
+        out << "#include <hidl/SynchronizedQueue.h>\n";
+    }
+
+    enterLeaveNamespace(out, true /* enter */);
+    out << "\n";
+
+    out << "struct "
+        << klassName
+        << " : " << ifaceName
+        << " {\n";
+
+    out.indent();
+    out << "explicit "
+        << klassName
+        << "(const sp<"
+        << ifaceName
+        << "> impl);\n";
+
+    status_t err = generateMethods(out,
+                                   "" /* class name */,
+                                   MethodLocation::PASSTHROUGH_HEADER,
+                                   true /* specify namespaces */);
+
+    if (err != OK) {
+        return err;
+    }
+
+    out.unindent();
+    out << "private:\n";
+    out.indent();
+    out << "const sp<" << ifaceName << "> mImpl;\n";
+
+    if (supportOneway) {
+        out << "SynchronizedQueue<std::function<void(void)>> mOnewayQueue;\n";
+        out << "std::thread *mOnewayThread = nullptr;\n";
+
+        out << "\n";
+
+        out << "::android::hardware::Return<void> addOnewayTask("
+               "std::function<void(void)>);\n\n";
+
+        out << "static const int kOnewayQueueMaxSize = 3000;\n";
+    }
+
+    out.unindent();
+
+    out << "};\n\n";
+
+    enterLeaveNamespace(out, false /* enter */);
+
+    out << "\n#endif  // " << guard << "\n";
+
+    return OK;
+}
+
+
+status_t AST::generatePassthroughSource(Formatter &out) const {
+    const Interface *iface = mRootScope->getInterface();
+
+    const std::string baseName = iface->getBaseName();
+    const std::string klassName = "Bs" + baseName;
+
+    out << klassName
+        << "::"
+        << klassName
+        << "(const sp<"
+        << iface->fullName()
+        << "> impl) : mImpl(impl) {}\n\n";
+
+    if (iface->hasOnewayMethods()) {
+        out << "::android::hardware::Return<void> "
+            << klassName
+            << "::addOnewayTask(std::function<void(void)> fun) {\n";
+        out.indent();
+        out << "if (mOnewayThread == nullptr) {\n";
+        out.indent();
+        out << "mOnewayThread = new std::thread([this]() {\n";
+        out.indent();
+        out << "while(true) { (this->mOnewayQueue.wait_pop())(); }";
+        out.unindent();
+        out << "});\n";
+        out.unindent();
+        out << "}\n\n";
+
+        out << "if (mOnewayQueue.size() > kOnewayQueueMaxSize) {\n";
+        out.indent();
+        out << "return Status::fromExceptionCode(Status::EX_TRANSACTION_FAILED);\n";
+        out.unindent();
+        out << "} else {\n";
+        out.indent();
+        out << "mOnewayQueue.push(fun);\n";
+        out.unindent();
+        out << "}\n";
+
+        out << "return Status();\n";
+
+        out.unindent();
+        out << "}\n\n";
+
+
+    }
+
+    return OK;
+}
+
 }  // namespace android