c++-impl: gen server stub boilerplate

-Lc++-impl now generates a default implementation for all
the interfaces in the specified package. The method
implementations are all left empty.

The default implementation also contains a special function,
HIDL_FETCH_IFOO(), which is used to retrieve an instance of
a particular interface in pass-through mode. For that to work,
the default implementation must live in a shared-library with
a well-known name, eg android.hardware.tests.foo@1.0.impl.so.

Sample invocation:
hidl-gen -o tmp/impl/ -Lc++-impl -randroid.hardware:hardware/interfaces
android.hardware.tests.foo@1.0

Bug: 31228745
Change-Id: I747aa47cb76931eed85179b8131d2efd3f887471
diff --git a/AST.h b/AST.h
index f62425a..3040c22 100644
--- a/AST.h
+++ b/AST.h
@@ -72,6 +72,7 @@
     void addImportedAST(AST *ast);
 
     status_t generateCpp(const std::string &outputPath) const;
+    status_t generateCppImpl(const std::string &outputPath) const;
 
     status_t generateJava(
             const std::string &outputPath, const char *limitToType) const;
@@ -146,9 +147,14 @@
 
     enum MethodLocation {
         PROXY_HEADER,
-        STUB_HEADER
+        STUB_HEADER,
+        IMPL_HEADER,
+        IMPL_SOURCE
     };
 
+    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,
@@ -157,10 +163,20 @@
                                 const std::string &className,
                                 const Method *method,
                                 bool specifyNamespaces) const;
-    status_t generateProxyMethod(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;
+    status_t generateStubImplMethod(Formatter &out,
+                                    const std::string &className,
+                                    const Method *method,
+                                    bool specifyNamespaces) const;
+
+    void generateFetchSymbol(Formatter &out, const std::string &ifaceName) const;
 
     status_t generateProxySource(
             Formatter &out, const std::string &baseName) const;
diff --git a/Android.mk b/Android.mk
index ce597f8..ab8efaf 100644
--- a/Android.mk
+++ b/Android.mk
@@ -15,6 +15,7 @@
     Formatter.cpp               \
     FQName.cpp                  \
     generateCpp.cpp             \
+    generateCppImpl.cpp         \
     generateJava.cpp            \
     generateVts.cpp             \
     GenericBinder.cpp           \
diff --git a/FQName.cpp b/FQName.cpp
index fcdaf20..bc47c46 100644
--- a/FQName.cpp
+++ b/FQName.cpp
@@ -193,6 +193,28 @@
     return out;
 }
 
+const FQName FQName::getTopLevelType() const {
+    auto idx = mName.find('.');
+
+    if (idx == std::string::npos) {
+        return *this;
+    }
+
+    return FQName(mPackage, mVersion, mName.substr(0, idx));
+}
+
+std::string FQName::tokenName() const {
+    std::vector<std::string> components;
+    getPackageAndVersionComponents(&components, true /* cpp_compatible */);
+
+    std::vector<std::string> nameComponents;
+    SplitString(mName, '.', &nameComponents);
+
+    components.insert(components.end(), nameComponents.begin(), nameComponents.end());
+
+    return JoinStrings(components, "_");
+}
+
 std::string FQName::cppNamespace() const {
     std::vector<std::string> components;
     getPackageAndVersionComponents(&components, true /* cpp_compatible */);
diff --git a/FQName.h b/FQName.h
index 8a0ad30..2fe7963 100644
--- a/FQName.h
+++ b/FQName.h
@@ -77,6 +77,18 @@
     bool operator<(const FQName &other) const;
     bool operator==(const FQName &other) const;
 
+    // the following comments all assume that the FQName
+    // is ::android::hardware::Foo::V1_0::IBar::Baz
+
+    // returns highest type in the hidl namespace, i.e.
+    // ::android::hardware::Foo::V1_0::IBar
+    const FQName getTopLevelType() const;
+
+    // returns an unambiguous fully qualified name which can be
+    // baked into a token, i.e.
+    // android_hardware_Foo_V1_0_IBar_Baz
+    std::string tokenName() const;
+
     // Returns an absolute C++ namespace prefix, i.e.
     // ::android::hardware::Foo::V1_0.
     std::string cppNamespace() const;
@@ -86,14 +98,14 @@
     std::string cppLocalName() const;
 
     // Returns a fully qualified absolute C++ type name, i.e.
-    // ::android::hardware::Foo::V1_0::bar::baz.
+    // ::android::hardware::Foo::V1_0::IBar::Baz.
     std::string cppName() const;
 
     // Returns the java package name, i.e. "android.hardware.Foo.V1_0".
     std::string javaPackage() const;
 
     // Returns the fully qualified java type name,
-    // i.e. "android.hardware.Foo.V1_0.IBar"
+    // i.e. "android.hardware.Foo.V1_0.IBar.Baz"
     std::string javaName() const;
 
     bool endsWith(const FQName &other) const;
diff --git a/generateCpp.cpp b/generateCpp.cpp
index 3668fd6..f11137c 100644
--- a/generateCpp.cpp
+++ b/generateCpp.cpp
@@ -74,15 +74,8 @@
 }
 
 std::string AST::makeHeaderGuard(const std::string &baseName) const {
-    std::vector<std::string> packageComponents;
-    getPackageAndVersionComponents(
-            &packageComponents, true /* cpp_compatible */);
-
-    std::string guard = "HIDL_GENERATED";
-    for (const auto &component : packageComponents) {
-        guard += "_";
-        guard += component;
-    }
+    std::string guard = "HIDL_GENERATED_";
+    guard += mPackage.tokenName();
 
     guard += "_";
     guard += baseName;
@@ -456,10 +449,10 @@
     return OK;
 }
 
-status_t AST::generateProxyMethod(Formatter &out,
-                                  const std::string &className,
-                                  const Method *method,
-                                  bool specifyNamespaces) const {
+status_t AST::generateProxyDeclaration(Formatter &out,
+                                       const std::string &className,
+                                       const Method *method,
+                                       bool specifyNamespaces) const {
 
     method->generateCppSignature(out,
                                  className,
@@ -500,12 +493,25 @@
                                              specifyNamespaces);
                     break;
                 case PROXY_HEADER:
-                    err = generateProxyMethod(out,
-                                              className,
-                                              method,
-                                              specifyNamespaces);
+                    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;
                 default:
+                    LOG(ERROR) << "Unkown method type: " << type;
                     err = UNKNOWN_ERROR;
             }
 
@@ -587,10 +593,15 @@
     out.unindent();
     out.unindent();
 
-    generateMethods(out,
-                    "" /* class name */,
-                    MethodLocation::STUB_HEADER,
-                    true /* specify namespaces */);
+    status_t err = generateMethods(out,
+                                   "" /* class name */,
+                                   MethodLocation::STUB_HEADER,
+                                   true /* specify namespaces */);
+
+    if (err != OK) {
+        return err;
+    }
+
     out.unindent();
 
     out << "};\n\n";
@@ -662,10 +673,14 @@
 
     out << "virtual bool isRemote() const { return true; }\n\n";
 
-    generateMethods(out,
-                    "" /* class name */,
-                    MethodLocation::PROXY_HEADER,
-                    true /* generate specify namespaces */);
+    status_t err = generateMethods(out,
+                                   "" /* class name */,
+                                   MethodLocation::PROXY_HEADER,
+                                   true /* generate specify namespaces */);
+
+    if (err != OK) {
+        return err;
+    }
 
     out.unindent();
 
@@ -745,7 +760,13 @@
     }
 
     if (err == OK && isInterface) {
-        out << "IMPLEMENT_REGISTER_AND_GET_SERVICE(" << baseName << ")\n";
+        const Interface *iface = mRootScope->getInterface();
+
+        out << "IMPLEMENT_REGISTER_AND_GET_SERVICE("
+            << baseName << ", "
+            << "\"" << iface->fqName().package()
+            << iface->fqName().version() << ".impl.so\""
+            << ")\n";
     }
 
     enterLeaveNamespace(out, false /* enter */);
diff --git a/generateCppImpl.cpp b/generateCppImpl.cpp
new file mode 100644
index 0000000..6c23e90
--- /dev/null
+++ b/generateCppImpl.cpp
@@ -0,0 +1,281 @@
+/*
+ * 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 "AST.h"
+
+#include "Coordinator.h"
+#include "EnumType.h"
+#include "Formatter.h"
+#include "Interface.h"
+#include "Method.h"
+#include "ScalarType.h"
+#include "Scope.h"
+
+#include <algorithm>
+#include <android-base/logging.h>
+#include <string>
+#include <vector>
+#include <set>
+
+
+namespace android {
+
+static std::string upcase(const std::string &in) {
+    std::string out{in};
+
+    for (auto &ch : out) {
+        ch = toupper(ch);
+    }
+
+    return out;
+}
+
+status_t AST::generateCppImpl(const std::string &outputPath) const {
+    status_t err = generateStubImplHeader(outputPath);
+
+    if (err == OK) {
+        err = generateStubImplSource(outputPath);
+    }
+
+    return err;
+}
+
+void AST::generateFetchSymbol(Formatter &out, const std::string& ifaceName) const {
+    out << "HIDL_FETCH_" << ifaceName;
+}
+
+status_t AST::generateStubImplMethod(Formatter &out,
+                                     const std::string &className,
+                                     const Method *method,
+                                     bool specifyNamespaces) const {
+
+    method->generateCppSignature(out, className, specifyNamespaces);
+
+    out << " {\n";
+
+    out.indent();
+    out << "// TODO implement\n";
+
+    const TypedVar *elidedReturn = method->canElideCallback();
+
+    if (elidedReturn == nullptr) {
+        out << "return Void();\n";
+    } else {
+        std::string extra;
+        out << "return "
+            << elidedReturn->type().getCppResultType(&extra)
+            << " {};\n";
+    }
+
+    out.unindent();
+
+    out << "}\n\n";
+
+    return OK;
+}
+
+status_t AST::generateStubImplDeclaration(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::generateStubImplHeader(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();
+
+    // cut off the leading 'I'.
+    const std::string baseName = ifaceName.substr(1);
+
+    std::string path = outputPath;
+    path.append(baseName);
+    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(baseName);
+
+    out << "#ifndef " << guard << "\n";
+    out << "#define " << guard << "\n\n";
+
+    std::vector<std::string> packageComponents;
+    getPackageAndVersionComponents(
+            &packageComponents, false /* cpp_compatible */);
+
+    out << "#include <";
+    for (const auto &component : packageComponents) {
+        out << component << "/";
+    }
+    out << "I" << baseName << ".h>\n";
+
+    out << "#include <hidl/Status.h>\n\n";
+
+    enterLeaveNamespace(out, true /* enter */);
+    out << "namespace implementation {\n\n";
+
+    // this is namespace aware code and doesn't require post-processing
+    out.setNamespace("");
+
+    const Interface *ancestor = iface;
+    std::vector<const Interface *> chain;
+    while (ancestor != NULL) {
+        chain.push_back(ancestor);
+        ancestor = ancestor->superType();
+    }
+
+    std::set<const FQName> usedTypes{};
+
+    for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
+        const Interface *superInterface = *it;
+
+        superInterface->addNamedTypesToSet(usedTypes);
+
+        for (const auto &method : superInterface->methods()) {
+            for(const auto & arg : method->args()) {
+                arg->type().addNamedTypesToSet(usedTypes);
+            }
+            for(const auto & results : method->results()) {
+                results->type().addNamedTypesToSet(usedTypes);
+            }
+        }
+    }
+
+    std::set<const FQName> topLevelTypes{};
+
+    for (const auto &name : usedTypes) {
+        topLevelTypes.insert(name.getTopLevelType());
+    }
+
+    for (const FQName &name : topLevelTypes) {
+        out << "using " << name.cppName() << ";\n";
+    }
+
+    out << "using ::android::hardware::Return;\n";
+    out << "using ::android::hardware::Void;\n";
+    out << "using ::android::hardware::hidl_vec;\n";
+    out << "using ::android::hardware::hidl_string;\n";
+    out << "using ::android::sp;\n";
+
+    out << "\n";
+
+    out << "struct "
+        << baseName
+        << " : public "
+        << ifaceName
+        << " {\n";
+
+    out.indent();
+
+    status_t err = generateMethods(out,
+                                   "", /* class name */
+                                   MethodLocation::IMPL_HEADER,
+                                   false /* specify namespaces */);
+
+    if (err != OK) {
+        return err;
+    }
+
+    out.unindent();
+
+    out << "};\n\n";
+
+    out << "extern \"C\" "
+        << ifaceName
+        << "* ";
+    generateFetchSymbol(out, ifaceName);
+    out << "(const char* name);\n\n";
+
+    out << "}  // namespace implementation\n";
+    enterLeaveNamespace(out, false /* leave */);
+
+    out << "\n#endif  // " << guard << "\n";
+
+    return OK;
+}
+
+status_t AST::generateStubImplSource(const std::string &outputPath) const {
+    std::string ifaceName;
+    if (!AST::isInterface(&ifaceName)) {
+        // types.hal does not get a stub header.
+        return OK;
+    }
+
+    // cut off the leading 'I'.
+    const std::string baseName = ifaceName.substr(1);
+
+    std::string path = outputPath;
+    path.append(baseName);
+    path.append(".cpp");
+
+    CHECK(Coordinator::MakeParentHierarchy(path));
+    FILE *file = fopen(path.c_str(), "w");
+
+    if (file == NULL) {
+        return -errno;
+    }
+
+    Formatter out(file);
+
+    out << "#include \"" << baseName << ".h\"\n\n";
+
+    enterLeaveNamespace(out, true /* enter */);
+    out << "namespace implementation {\n\n";
+
+    // this is namespace aware code and doesn't require post-processing
+    out.setNamespace("");
+
+    generateMethods(out,
+                    baseName,
+                    MethodLocation::IMPL_SOURCE,
+                    false /* specify namespaces */);
+
+    const Interface *iface = mRootScope->getInterface();
+
+    out << ifaceName
+        << "* ";
+    generateFetchSymbol(out, ifaceName);
+    out << "(const char* /* name */) {\n";
+    out.indent();
+    out << "return new " << baseName << "();\n";
+    out.unindent();
+    out << "}\n\n";
+
+    out << "} // namespace implementation\n";
+    enterLeaveNamespace(out, false /* leave */);
+
+    return OK;
+}
+
+}  // namespace android
diff --git a/main.cpp b/main.cpp
index e9c07b7..6e5d3dc 100644
--- a/main.cpp
+++ b/main.cpp
@@ -77,6 +77,9 @@
     if (lang == "c++") {
         return ast->generateCpp(outputDir);
     }
+    if (lang == "c++-impl") {
+        return ast->generateCppImpl(outputDir);
+    }
     if (lang == "java") {
         return ast->generateJava(outputDir, limitToType);
     }
@@ -466,6 +469,27 @@
         }
     },
 
+    {"c++-impl",
+     true /* mNeedsOutputDir */,
+     validateForSource,
+     [](const FQName &fqName,
+        const char *hidl_gen, Coordinator *coordinator,
+        const std::string &outputDir) -> status_t {
+            if (fqName.isFullyQualified()) {
+                        return generateSourcesForFile(fqName,
+                                                      hidl_gen,
+                                                      coordinator,
+                                                      outputDir, "c++-impl");
+            } else {
+                        return generateSourcesForPackage(fqName,
+                                                         hidl_gen,
+                                                         coordinator,
+                                                         outputDir, "c++-impl");
+            }
+        }
+    },
+
+
     {"java",
      true /* mNeedsOutputDir */,
      validateForSource,