Oneway keyword behavior in passthrough mode.

Added BsFoo class to output. This acts as a proxy similar to BpFoo,
however instead of delegating tasks accross the binder interface, it
does so directly to the implementation. The only added behavior is using
doing queueing up these function calls on another thread so that
the asynchronous behavior will match the binderized case.

BUG: 31237398

Change-Id: I099824f0f754089c824b368a188f8125a111f840
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