Merge changes from topic 'ibase_hash' into oc-dev

* changes:
  Add getHashChain() to IBase.
  Add Formatter::operator<< for integer types / char
  Add Formatter::join
diff --git a/Android.bp b/Android.bp
index 6db4821..1c68952 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,8 +56,10 @@
     ],
     shared_libs: [
         "libbase",
+        "libcrypto",
         "liblog",
         "libhidl-gen-utils",
+        "libssl",
     ],
 }
 
diff --git a/Annotation.cpp b/Annotation.cpp
index 1205079..a3e49f8 100644
--- a/Annotation.cpp
+++ b/Annotation.cpp
@@ -18,6 +18,7 @@
 
 #include <android-base/logging.h>
 #include <hidl-util/Formatter.h>
+#include <hidl-util/StringHelper.h>
 #include <vector>
 
 namespace android {
@@ -120,16 +121,7 @@
             out << "{";
         }
 
-        bool first = true;
-        for (const auto &value : *values) {
-            if (!first) {
-                out << ", ";
-            }
-
-            out << value;
-
-            first = false;
-        }
+        out << StringHelper::JoinStrings(*values, ", ");
 
         if (values->size() > 1) {
             out << "}";
diff --git a/ArrayType.cpp b/ArrayType.cpp
index 8b75c94..3d34a9f 100644
--- a/ArrayType.cpp
+++ b/ArrayType.cpp
@@ -96,6 +96,16 @@
     CHECK(!"Should not be here");
 }
 
+std::string ArrayType::getInternalDataCppType() const {
+    std::string result = mElementType->getCppStackType();
+    for (size_t i = 0; i < mSizes.size(); ++i) {
+        result += "[";
+        result += mSizes[i]->cppValue();
+        result += "]";
+    }
+    return result;
+}
+
 std::string ArrayType::getJavaType(bool forInitializer) const {
     std::string base =
         mElementType->getJavaType(forInitializer);
diff --git a/ArrayType.h b/ArrayType.h
index ae58d28..3bb091f 100644
--- a/ArrayType.h
+++ b/ArrayType.h
@@ -44,6 +44,8 @@
     std::string getCppType(StorageMode mode,
                            bool specifyNamespaces) const override;
 
+    std::string getInternalDataCppType() const;
+
     void addNamedTypesToSet(std::set<const FQName> &set) const override;
 
     std::string getJavaType(bool forInitializer) const override;
diff --git a/CompoundType.cpp b/CompoundType.cpp
index 3d2a697..1bd9aef 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -605,14 +605,9 @@
         out.block([&] {
             out << "return java.util.Objects.hash(\n";
             out.indent(2, [&] {
-                bool first = true;
-                for (const auto &field : *mFields) {
-                    if (!first) {
-                        out << ", \n";
-                    }
-                    first = false;
+                out.join(mFields->begin(), mFields->end(), ", \n", [&] (const auto &field) {
                     out << "android.os.HidlSupport.deepHashCode(this." << field->name() << ")";
-                }
+                });
             });
             out << ");\n";
         }).endl().endl();
diff --git a/ConstantExpression.cpp b/ConstantExpression.cpp
index 850faf8..084f9df 100644
--- a/ConstantExpression.cpp
+++ b/ConstantExpression.cpp
@@ -152,19 +152,32 @@
 ConstantExpression::ConstantExpression() {
 }
 
+// static
 ConstantExpression ConstantExpression::Zero(ScalarType::Kind kind) {
-    ConstantExpression ce("0");
-    CHECK(isSupported(kind));
-    ce.mValueKind = kind;
-    return ce;
-}
-ConstantExpression ConstantExpression::One(ScalarType::Kind kind) {
-    ConstantExpression ce("1");
-    CHECK(isSupported(kind));
-    ce.mValueKind = kind;
+    ConstantExpression ce = ValueOf(kind, 0);
+    ce.mExpr = "0";
     return ce;
 }
 
+// static
+ConstantExpression ConstantExpression::One(ScalarType::Kind kind) {
+    ConstantExpression ce = ValueOf(kind, 1);
+    ce.mExpr = "1";
+    return ce;
+}
+
+// static
+ConstantExpression ConstantExpression::ValueOf(ScalarType::Kind kind, uint64_t value) {
+    ConstantExpression ce;
+    CHECK(isSupported(kind));
+
+    ce.mExpr = "";
+    ce.mType = kConstExprLiteral;
+    ce.mValueKind = kind;
+    ce.mValue = value;
+    ce.mTrivialDescription = true;
+    return ce;
+}
 ConstantExpression::ConstantExpression(const ConstantExpression& other) {
     *this = other;
 }
diff --git a/ConstantExpression.h b/ConstantExpression.h
index 1f0524b..2c955d4 100644
--- a/ConstantExpression.h
+++ b/ConstantExpression.h
@@ -56,6 +56,7 @@
 
     static ConstantExpression Zero(ScalarType::Kind kind);
     static ConstantExpression One(ScalarType::Kind kind);
+    static ConstantExpression ValueOf(ScalarType::Kind kind, uint64_t value);
 
     /* Evaluated result in a string form. */
     std::string value() const;
diff --git a/Interface.cpp b/Interface.cpp
index 6cd6e94..d326703 100644
--- a/Interface.cpp
+++ b/Interface.cpp
@@ -17,17 +17,24 @@
 #include "Interface.h"
 
 #include "Annotation.h"
+#include "ArrayType.h"
+#include "ConstantExpression.h"
 #include "DeathRecipientType.h"
 #include "Method.h"
 #include "ScalarType.h"
 #include "StringType.h"
 #include "VectorType.h"
 
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
 #include <android-base/logging.h>
 #include <hidl-util/Formatter.h>
 #include <hidl-util/StringHelper.h>
-#include <iostream>
-#include <sstream>
+#include <openssl/sha.h>
 
 namespace android {
 
@@ -58,6 +65,7 @@
     HIDL_SET_HAL_INSTRUMENTATION_TRANSACTION  = B_PACK_CHARS(0x0f, 'I', 'N', 'T'),
     HIDL_GET_REF_INFO_TRANSACTION             = B_PACK_CHARS(0x0f, 'R', 'E', 'F'),
     HIDL_DEBUG_TRANSACTION                    = B_PACK_CHARS(0x0f, 'D', 'B', 'G'),
+    HIDL_HASH_CHAIN_TRANSACTION               = B_PACK_CHARS(0x0f, 'H', 'S', 'H'),
     LAST_HIDL_TRANSACTION   = 0x0fffffff,
 };
 
@@ -280,6 +288,75 @@
     return true;
 }
 
+static void sha256File(const std::string &path, uint8_t *outDigest) {
+    std::ifstream stream(path);
+    std::stringstream fileStream;
+    fileStream << stream.rdbuf();
+    std::string fileContent = fileStream.str();
+    SHA256(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
+            fileContent.size(), outDigest);
+}
+
+static void emitDigestChain(
+        Formatter &out,
+        const std::string &prefix,
+        const std::vector<const Interface *> &chain,
+        std::function<std::string(const ConstantExpression &)> byteToString) {
+    out.join(chain.begin(), chain.end(), ",\n", [&] (const auto &iface) {
+        const std::string &filename = iface->location().begin().filename();
+        uint8_t digest[SHA256_DIGEST_LENGTH];
+        sha256File(filename, digest);
+        out << prefix;
+        out << "{";
+        out.join(digest, digest + SHA256_DIGEST_LENGTH, ",", [&](const auto &e) {
+            // Use ConstantExpression::cppValue / javaValue
+            // because Java used signed byte for uint8_t.
+            out << byteToString(ConstantExpression::ValueOf(ScalarType::Kind::KIND_UINT8, e));
+        });
+        out << "} /* ";
+        out.join(digest, digest + SHA256_DIGEST_LENGTH, "", [&](const auto &e) {
+            static const char hexes[] = "0123456789abcdef";
+            out << hexes[e >> 4] << hexes[e & 0xF];
+        });
+        out << " */";
+    });
+}
+
+bool Interface::fillHashChainMethod(Method *method) const {
+    if (method->name() != "getHashChain") {
+        return false;
+    }
+    const VectorType *chainType = static_cast<const VectorType *>(&method->results()[0]->type());
+    const ArrayType *digestType = static_cast<const ArrayType *>(chainType->getElementType());
+
+    method->fillImplementation(
+        HIDL_HASH_CHAIN_TRANSACTION,
+        { { IMPL_INTERFACE, [this, digestType](auto &out) {
+            std::vector<const Interface *> chain = typeChain();
+            out << "_hidl_cb(";
+            out.block([&] {
+                emitDigestChain(out, "(" + digestType->getInternalDataCppType() + ")",
+                    chain, [](const auto &e){return e.cppValue();});
+            });
+            out << ");\n";
+            out << "return ::android::hardware::Void();\n";
+        } } }, /* cppImpl */
+        { { IMPL_INTERFACE, [this, digestType, chainType](auto &out) {
+            std::vector<const Interface *> chain = typeChain();
+            out << "return new "
+                << chainType->getJavaType(false /* forInitializer */)
+                << "(java.util.Arrays.asList(\n";
+            out.indent(2, [&] {
+                // No need for dimensions when elements are explicitly provided.
+                emitDigestChain(out, "new " + digestType->getJavaType(false /* forInitializer */),
+                    chain, [](const auto &e){return e.javaValue();});
+            });
+            out << "));\n";
+        } } } /* javaImpl */
+    );
+    return true;
+}
+
 bool Interface::fillGetDescriptorMethod(Method *method) const {
     if (method->name() != "interfaceDescriptor") {
         return false;
@@ -424,6 +501,7 @@
         bool fillSuccess = fillPingMethod(method)
             || fillDescriptorChainMethod(method)
             || fillGetDescriptorMethod(method)
+            || fillHashChainMethod(method)
             || fillSyspropsChangedMethod(method)
             || fillLinkToDeathMethod(method)
             || fillUnlinkToDeathMethod(method)
diff --git a/Interface.h b/Interface.h
index 1b3ebb5..793226a 100644
--- a/Interface.h
+++ b/Interface.h
@@ -117,6 +117,7 @@
     bool fillPingMethod(Method *method) const;
     bool fillDescriptorChainMethod(Method *method) const;
     bool fillGetDescriptorMethod(Method *method) const;
+    bool fillHashChainMethod(Method *method) const;
     bool fillSyspropsChangedMethod(Method *method) const;
     bool fillLinkToDeathMethod(Method *method) const;
     bool fillUnlinkToDeathMethod(Method *method) const;
diff --git a/Method.cpp b/Method.cpp
index c967d89..ab080b1 100644
--- a/Method.cpp
+++ b/Method.cpp
@@ -139,8 +139,8 @@
     }
 
     out << name()
-        << "("
-        << GetArgSignature(args(), specifyNamespaces);
+        << "(";
+    emitCppArgSignature(out, specifyNamespaces);
 
     if (returnsValue && elidedReturn == nullptr) {
         if (!args().empty()) {
@@ -153,43 +153,35 @@
     out << ")";
 }
 
-// static
-std::string Method::GetArgSignature(const std::vector<TypedVar *> &args,
-                                    bool specifyNamespaces) {
-    bool first = true;
-    std::string out;
-    for (const auto &arg : args) {
-        if (!first) {
-            out += ", ";
-        }
-
-        out += arg->type().getCppArgumentType(specifyNamespaces);
-        out += " ";
-        out += arg->name();
-
-        first = false;
-    }
-
-    return out;
+static void emitCppArgResultSignature(Formatter &out,
+                         const std::vector<TypedVar *> &args,
+                         bool specifyNamespaces) {
+    out.join(args.begin(), args.end(), ", ", [&](auto arg) {
+        out << arg->type().getCppArgumentType(specifyNamespaces);
+        out << " ";
+        out << arg->name();
+    });
 }
 
-// static
-std::string Method::GetJavaArgSignature(const std::vector<TypedVar *> &args) {
-    bool first = true;
-    std::string out;
-    for (const auto &arg : args) {
-        if (!first) {
-            out += ", ";
-        }
+static void emitJavaArgResultSignature(Formatter &out, const std::vector<TypedVar *> &args) {
+    out.join(args.begin(), args.end(), ", ", [&](auto arg) {
+        out << arg->type().getJavaType();
+        out << " ";
+        out << arg->name();
+    });
+}
 
-        out += arg->type().getJavaType();
-        out += " ";
-        out += arg->name();
-
-        first = false;
-    }
-
-    return out;
+void Method::emitCppArgSignature(Formatter &out, bool specifyNamespaces) const {
+    emitCppArgResultSignature(out, args(), specifyNamespaces);
+}
+void Method::emitCppResultSignature(Formatter &out, bool specifyNamespaces) const {
+    emitCppArgResultSignature(out, results(), specifyNamespaces);
+}
+void Method::emitJavaArgSignature(Formatter &out) const {
+    emitJavaArgResultSignature(out, args());
+}
+void Method::emitJavaResultSignature(Formatter &out) const {
+    emitJavaArgResultSignature(out, results());
 }
 
 void Method::dumpAnnotations(Formatter &out) const {
diff --git a/Method.h b/Method.h
index 229f042..5760d80 100644
--- a/Method.h
+++ b/Method.h
@@ -82,9 +82,10 @@
                               const std::string &className = "",
                               bool specifyNamespaces = true) const;
 
-    static std::string GetArgSignature(const std::vector<TypedVar *> &args,
-                                       bool specifyNamespaces);
-    static std::string GetJavaArgSignature(const std::vector<TypedVar *> &args);
+    void emitCppArgSignature(Formatter &out, bool specifyNamespaces) const;
+    void emitCppResultSignature(Formatter &out, bool specifyNamespaces) const;
+    void emitJavaArgSignature(Formatter &out) const;
+    void emitJavaResultSignature(Formatter &out) const;
 
     const TypedVar* canElideCallback() const;
 
diff --git a/generateCpp.cpp b/generateCpp.cpp
index 8df7b54..51ebdc5 100644
--- a/generateCpp.cpp
+++ b/generateCpp.cpp
@@ -432,10 +432,9 @@
             if (elidedReturn == nullptr && returnsValue) {
                 out << "using "
                     << method->name()
-                    << "_cb = std::function<void("
-                    << Method::GetArgSignature(method->results(),
-                                               true /* specify namespaces */)
-                    << ")>;\n";
+                    << "_cb = std::function<void(";
+                method->emitCppResultSignature(out, true /* specify namespaces */);
+                out << ")>;\n";
             }
 
             method->dumpAnnotations(out);
@@ -448,9 +447,8 @@
             }
 
             out << method->name()
-                << "("
-                << Method::GetArgSignature(method->args(),
-                                           true /* specify namespaces */);
+                << "(";
+            method->emitCppArgSignature(out, true /* specify namespaces */);
 
             if (returnsValue && elidedReturn == nullptr) {
                 if (!method->args().empty()) {
@@ -671,30 +669,18 @@
         << method->name()
         << "(";
 
-    bool first = true;
-    for (const auto &arg : method->args()) {
-        if (!first) {
-            out << ", ";
-        }
-        first = false;
+    out.join(method->args().begin(), method->args().end(), ", ", [&](const auto &arg) {
         out << (arg->type().isInterface() ? "_hidl_wrapped_" : "") << arg->name();
-    }
+    });
     if (returnsValue && elidedReturn == nullptr) {
         if (!method->args().empty()) {
             out << ", ";
         }
         out << "[&](";
-        first = true;
-        for (const auto &arg : method->results()) {
-            if (!first) {
-                out << ", ";
-            }
-
+        out.join(method->results().begin(), method->results().end(), ", ", [&](const auto &arg) {
             out << "const auto &_hidl_out_"
                 << arg->name();
-
-            first = false;
-        }
+        });
 
         out << ") {\n";
         out.indent();
@@ -718,15 +704,10 @@
         }
 
         out << "_hidl_cb(";
-        first = true;
-        for (const auto &arg : method->results()) {
-            if (!first) {
-                out << ", ";
-            }
-            first = false;
+        out.join(method->results().begin(), method->results().end(), ", ", [&](const auto &arg) {
             out << (arg->type().isInterface() ? "_hidl_out_wrapped_" : "_hidl_out_")
                 << arg->name();
-        }
+        });
         out << ");\n";
         out.unindent();
         out << "});\n\n";
@@ -1342,19 +1323,12 @@
         if (returnsValue && elidedReturn == nullptr) {
             out << "_hidl_cb(";
 
-            bool first = true;
-            for (const auto &arg : method->results()) {
-                if (!first) {
-                    out << ", ";
-                }
-
+            out.join(method->results().begin(), method->results().end(), ", ", [&] (const auto &arg) {
                 if (arg->type().resultNeedsDeref()) {
                     out << "*";
                 }
                 out << "_hidl_out_" << arg->name();
-
-                first = false;
-            }
+            });
 
             out << ");\n\n";
         }
@@ -1641,20 +1615,12 @@
             << callee << "->" << method->name()
             << "(";
 
-        bool first = true;
-        for (const auto &arg : method->args()) {
-            if (!first) {
-                out << ", ";
-            }
-
+        out.join(method->args().begin(), method->args().end(), ", ", [&] (const auto &arg) {
             if (arg->type().resultNeedsDeref()) {
                 out << "*";
             }
-
             out << arg->name();
-
-            first = false;
-        }
+        });
 
         out << ");\n\n";
         out << "::android::hardware::writeToParcel(::android::hardware::Status::ok(), "
@@ -1693,38 +1659,24 @@
 
         out << callee << "->" << method->name() << "(";
 
-        bool first = true;
-        for (const auto &arg : method->args()) {
-            if (!first) {
-                out << ", ";
-            }
-
+        out.join(method->args().begin(), method->args().end(), ", ", [&] (const auto &arg) {
             if (arg->type().resultNeedsDeref()) {
                 out << "*";
             }
 
             out << arg->name();
-
-            first = false;
-        }
+        });
 
         if (returnsValue) {
-            if (!first) {
+            if (!method->args().empty()) {
                 out << ", ";
             }
 
             out << "[&](";
 
-            first = true;
-            for (const auto &arg : method->results()) {
-                if (!first) {
-                    out << ", ";
-                }
-
+            out.join(method->results().begin(), method->results().end(), ", ", [&](const auto &arg) {
                 out << "const auto &_hidl_out_" << arg->name();
-
-                first = false;
-            }
+            });
 
             out << ") {\n";
             out.indent();
diff --git a/generateJava.cpp b/generateJava.cpp
index 3d6e781..6e5d91b 100644
--- a/generateJava.cpp
+++ b/generateJava.cpp
@@ -248,9 +248,9 @@
 
             out.indent();
 
-            out << "public void onValues("
-                << Method::GetJavaArgSignature(method->results())
-                << ");\n";
+            out << "public void onValues(";
+            method->emitJavaResultSignature(out);
+            out << ");\n";
 
             out.unindent();
             out << "}\n\n";
@@ -264,8 +264,8 @@
 
         out << " "
             << method->name()
-            << "("
-            << Method::GetJavaArgSignature(method->args());
+            << "(";
+        method->emitJavaArgSignature(out);
 
         if (needsCallback) {
             if (!method->args().empty()) {
@@ -340,8 +340,8 @@
 
         out << " "
             << method->name()
-            << "("
-            << Method::GetJavaArgSignature(method->args());
+            << "(";
+        method->emitJavaArgSignature(out);
 
         if (needsCallback) {
             if (!method->args().empty()) {
@@ -472,9 +472,9 @@
             << resultType
             << " "
             << method->name()
-            << "("
-            << Method::GetJavaArgSignature(method->args())
-            << ") {\n";
+            << "(";
+        method->emitJavaArgSignature(out);
+        out << ") {\n";
 
         out.indent();
         method->javaImpl(IMPL_INTERFACE, out);
@@ -604,9 +604,9 @@
             out.indent();
 
             out << "@Override\n"
-                << "public void onValues("
-                << Method::GetJavaArgSignature(method->results())
-                << ") {\n";
+                << "public void onValues(";
+            method->emitJavaResultSignature(out);
+            out << ") {\n";
 
             out.indent();
             out << "_hidl_reply.writeStatus(android.os.HwParcel.STATUS_SUCCESS);\n";
diff --git a/test/Android.bp b/test/Android.bp
index c392290..fbff8d0 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -23,6 +23,7 @@
         "android.hardware.tests.foo@1.0",
         "android.hardware.tests.bar@1.0",
         "android.hardware.tests.baz@1.0",
+        "android.hardware.tests.hash@1.0",
         "android.hardware.tests.inheritance@1.0",
         "android.hardware.tests.pointer@1.0",
         "android.hardware.tests.memory@1.0",
@@ -40,6 +41,7 @@
         "android.hardware.tests.foo@1.0-impl",
         "android.hardware.tests.bar@1.0-impl",
         "android.hardware.tests.baz@1.0-impl",
+        "android.hardware.tests.hash@1.0-impl",
         "android.hardware.tests.inheritance@1.0-impl",
         "android.hardware.tests.pointer@1.0-impl",
         "android.hardware.tests.memory@1.0-impl",
@@ -61,6 +63,7 @@
         "android.hardware.tests.foo@1.0",
         "android.hardware.tests.bar@1.0",
         "android.hardware.tests.baz@1.0",
+        "android.hardware.tests.hash@1.0",
         "android.hardware.tests.inheritance@1.0",
         "android.hardware.tests.pointer@1.0",
         "android.hardware.tests.memory@1.0",
diff --git a/test/hidl_test_client.cpp b/test/hidl_test_client.cpp
index 0dcfc32..76c87ed 100644
--- a/test/hidl_test_client.cpp
+++ b/test/hidl_test_client.cpp
@@ -20,6 +20,7 @@
 #include <android/hardware/tests/bar/1.0/IComplicated.h>
 #include <android/hardware/tests/bar/1.0/IImportRules.h>
 #include <android/hardware/tests/baz/1.0/IBaz.h>
+#include <android/hardware/tests/hash/1.0/IHash.h>
 #include <android/hardware/tests/inheritance/1.0/IFetcher.h>
 #include <android/hardware/tests/inheritance/1.0/IGrandparent.h>
 #include <android/hardware/tests/inheritance/1.0/IParent.h>
@@ -84,6 +85,7 @@
 using ::android::hardware::tests::bar::V1_0::IBar;
 using ::android::hardware::tests::bar::V1_0::IComplicated;
 using ::android::hardware::tests::baz::V1_0::IBaz;
+using ::android::hardware::tests::hash::V1_0::IHash;
 using ::android::hardware::tests::inheritance::V1_0::IFetcher;
 using ::android::hardware::tests::inheritance::V1_0::IGrandparent;
 using ::android::hardware::tests::inheritance::V1_0::IParent;
@@ -507,6 +509,21 @@
     ASSERT_NE(manager, nullptr);
 }
 
+TEST_F(HidlTest, HashTest) {
+    uint8_t ihash[32] = {74,38,204,105,102,117,11,15,207,7,238,198,29,35,30,62,100,
+            216,131,182,3,61,162,241,215,211,6,20,251,143,125,161};
+    auto service = IHash::getService(mode == PASSTHROUGH /* getStub */);
+    EXPECT_OK(service->getHashChain([&] (const auto &chain) {
+        EXPECT_EQ(chain[0].size(), 32u);
+        EXPECT_ARRAYEQ(ihash, chain[0], 32);
+        EXPECT_OK(manager->getHashChain([&] (const auto &managerChain) {
+            EXPECT_EQ(chain[chain.size() - 1].size(), managerChain[managerChain.size() - 1].size());
+            EXPECT_ARRAYEQ(chain[chain.size() - 1], managerChain[managerChain.size() - 1],
+                    chain[chain.size() - 1].size()) << "Hash for IBase doesn't match!";
+        }));
+    }));
+}
+
 TEST_F(HidlTest, ServiceListTest) {
     static const std::set<std::string> binderizedSet = {
         "android.hardware.tests.pointer@1.0::IPointer/pointer",
diff --git a/test/hidl_test_servers.cpp b/test/hidl_test_servers.cpp
index 5aa46bd..21f9721 100644
--- a/test/hidl_test_servers.cpp
+++ b/test/hidl_test_servers.cpp
@@ -23,6 +23,7 @@
 #include <android/hardware/tests/foo/1.0/BpHwSimple.h>
 #include <android/hardware/tests/bar/1.0/IBar.h>
 #include <android/hardware/tests/baz/1.0/IBaz.h>
+#include <android/hardware/tests/hash/1.0/IHash.h>
 #include <android/hardware/tests/inheritance/1.0/IFetcher.h>
 #include <android/hardware/tests/inheritance/1.0/IParent.h>
 #include <android/hardware/tests/inheritance/1.0/IChild.h>
@@ -43,6 +44,7 @@
 
 using ::android::hardware::tests::bar::V1_0::IBar;
 using ::android::hardware::tests::baz::V1_0::IBaz;
+using ::android::hardware::tests::hash::V1_0::IHash;
 using ::android::hardware::tests::inheritance::V1_0::IFetcher;
 using ::android::hardware::tests::inheritance::V1_0::IParent;
 using ::android::hardware::tests::inheritance::V1_0::IChild;
@@ -108,6 +110,7 @@
     forkServer<IParent>("parent");
     forkServer<IFetcher>("fetcher");
     forkServer<IBar>("foo");
+    forkServer<IHash>("default");
     forkServer<IBaz>("dyingBaz");
     forkServer<IGraph>("graph");
     forkServer<IPointer>("pointer");
diff --git a/utils/Android.bp b/utils/Android.bp
index e4f718a..b3913ee 100644
--- a/utils/Android.bp
+++ b/utils/Android.bp
@@ -17,9 +17,9 @@
     host_supported: true,
     cflags: hidl_flags,
     srcs: [
+        "FQName.cpp",
         "Formatter.cpp",
         "StringHelper.cpp",
-        "FQName.cpp"
     ],
     shared_libs: [
         "libbase",
diff --git a/utils/Formatter.cpp b/utils/Formatter.cpp
index 503d145..a4f2047 100644
--- a/utils/Formatter.cpp
+++ b/utils/Formatter.cpp
@@ -148,9 +148,35 @@
     return *this;
 }
 
-Formatter &Formatter::operator<<(size_t n) {
-    return (*this) << std::to_string(n);
-}
+#define FORMATTER_INPUT_INTEGER(__type__)               \
+    Formatter &Formatter::operator<<(__type__ n) {      \
+        return (*this) << std::to_string(n);            \
+    }                                                   \
+
+FORMATTER_INPUT_INTEGER(short);
+FORMATTER_INPUT_INTEGER(unsigned short);
+FORMATTER_INPUT_INTEGER(int);
+FORMATTER_INPUT_INTEGER(unsigned int);
+FORMATTER_INPUT_INTEGER(long);
+FORMATTER_INPUT_INTEGER(unsigned long);
+FORMATTER_INPUT_INTEGER(long long);
+FORMATTER_INPUT_INTEGER(unsigned long long);
+FORMATTER_INPUT_INTEGER(float);
+FORMATTER_INPUT_INTEGER(double);
+FORMATTER_INPUT_INTEGER(long double);
+
+#undef FORMATTER_INPUT_INTEGER
+
+#define FORMATTER_INPUT_CHAR(__type__)                  \
+    Formatter &Formatter::operator<<(__type__ c) {      \
+        return (*this) << std::string(1, (char)c);    \
+    }                                                   \
+
+FORMATTER_INPUT_CHAR(char);
+FORMATTER_INPUT_CHAR(signed char);
+FORMATTER_INPUT_CHAR(unsigned char);
+
+#undef FORMATTER_INPUT_CHAR
 
 void Formatter::setNamespace(const std::string &space) {
     mSpace = space;
diff --git a/utils/include/hidl-util/Formatter.h b/utils/include/hidl-util/Formatter.h
index 57fb3b6..513efad 100644
--- a/utils/include/hidl-util/Formatter.h
+++ b/utils/include/hidl-util/Formatter.h
@@ -101,8 +101,30 @@
     // }).endl();
     Formatter &sWhile(const std::string &cond, std::function<void(void)> block);
 
+    // out.join(v.begin(), v.end(), ",", [&](const auto &e) {
+    //     out << toString(e);
+    // });
+    template<typename I>
+    Formatter &join(I begin, I end, const std::string &separator,
+            std::function<void(const typename std::iterator_traits<I>::value_type &)> func);
+
     Formatter &operator<<(const std::string &out);
-    Formatter &operator<<(size_t n);
+
+    Formatter &operator<<(char c);
+    Formatter &operator<<(signed char c);
+    Formatter &operator<<(unsigned char c);
+
+    Formatter &operator<<(short c);
+    Formatter &operator<<(unsigned short c);
+    Formatter &operator<<(int c);
+    Formatter &operator<<(unsigned int c);
+    Formatter &operator<<(long c);
+    Formatter &operator<<(unsigned long c);
+    Formatter &operator<<(long long c);
+    Formatter &operator<<(unsigned long long c);
+    Formatter &operator<<(float c);
+    Formatter &operator<<(double c);
+    Formatter &operator<<(long double c);
 
     // Any substrings matching "space" will be stripped out of the output.
     void setNamespace(const std::string &space);
@@ -128,6 +150,18 @@
     DISALLOW_COPY_AND_ASSIGN(Formatter);
 };
 
+template<typename I>
+Formatter &Formatter::join(I begin, I end, const std::string &separator,
+        std::function<void(const typename std::iterator_traits<I>::value_type &)> func) {
+    for (I iter = begin; iter != end; ++iter) {
+        if (iter != begin) {
+            (*this) << separator;
+        }
+        func(*iter);
+    }
+    return (*this);
+}
+
 }  // namespace android
 
 #endif  // FORMATTER_H_