Initial commit of Java backend to hidl-gen.

Change-Id: I38b62637df74d3e5daf702a8996502d0d5726033
diff --git a/AST.h b/AST.h
index 026d90b..65dd016 100644
--- a/AST.h
+++ b/AST.h
@@ -54,6 +54,7 @@
     Type *lookupTypeInternal(const std::string &namePath) const;
 
     status_t generateCpp(const std::string &outputPath) const;
+    status_t generateJava(const std::string &outputPath) const;
 
     void getImportedPackages(std::set<FQName> *importSet) const;
 
@@ -101,7 +102,14 @@
             bool isReader,
             Type::ErrorMode mode) const;
 
+    void emitJavaReaderWriter(
+            Formatter &out,
+            const std::string &parcelObj,
+            const TypedVar *arg,
+            bool isReader) const;
+
     status_t emitTypeDeclarations(Formatter &out) const;
+    status_t emitJavaTypeDeclarations(Formatter &out) const;
 
     DISALLOW_COPY_AND_ASSIGN(AST);
 };
diff --git a/Android.mk b/Android.mk
index a0496eb..8ecfa50 100644
--- a/Android.mk
+++ b/Android.mk
@@ -15,6 +15,7 @@
     Formatter.cpp               \
     FQName.cpp                  \
     generateCpp.cpp             \
+    generateJava.cpp            \
     HandleType.cpp              \
     hidl-gen_y.yy               \
     hidl-gen_l.ll               \
diff --git a/ArrayType.cpp b/ArrayType.cpp
index 732f2e6..3679429 100644
--- a/ArrayType.cpp
+++ b/ArrayType.cpp
@@ -33,6 +33,10 @@
     }
 }
 
+std::string ArrayType::getJavaType() const {
+    return mElementType->getJavaType() + "[]";
+}
+
 void ArrayType::emitReaderWriter(
         Formatter &out,
         const std::string &name,
@@ -142,5 +146,19 @@
     return mElementType->needsEmbeddedReadWrite();
 }
 
+void ArrayType::emitJavaReaderWriter(
+        Formatter &out,
+        const std::string &parcelObj,
+        const std::string &argName,
+        bool isReader) const {
+    emitJavaReaderWriterWithSuffix(
+            out,
+            parcelObj,
+            argName,
+            isReader,
+            mElementType->getJavaSuffix() + "Array",
+            mDimension);
+}
+
 }  // namespace android
 
diff --git a/ArrayType.h b/ArrayType.h
index c95204b..0a245f8 100644
--- a/ArrayType.h
+++ b/ArrayType.h
@@ -13,6 +13,8 @@
 
     std::string getCppType(StorageMode mode, std::string *extra) const override;
 
+    std::string getJavaType() const override;
+
     void emitReaderWriter(
             Formatter &out,
             const std::string &name,
@@ -34,6 +36,12 @@
 
     bool needsEmbeddedReadWrite() const override;
 
+    void emitJavaReaderWriter(
+            Formatter &out,
+            const std::string &parcelObj,
+            const std::string &argName,
+            bool isReader) const override;
+
 private:
     Type *mElementType;
     std::string mDimension;
diff --git a/EnumType.cpp b/EnumType.cpp
index 95cb6be..2c6f729 100644
--- a/EnumType.cpp
+++ b/EnumType.cpp
@@ -38,6 +38,14 @@
     return fullName();
 }
 
+std::string EnumType::getJavaType() const {
+    return mStorageType->resolveToScalarType()->getJavaType();
+}
+
+std::string EnumType::getJavaSuffix() const {
+    return mStorageType->resolveToScalarType()->getJavaSuffix();
+}
+
 void EnumType::emitReaderWriter(
         Formatter &out,
         const std::string &name,
@@ -106,6 +114,94 @@
     return OK;
 }
 
+// Attempt to convert enum value literals into their signed equivalents,
+// i.e. if an enum value is stored in typeName 'byte', the value "192"
+// will be converted to the output "-64".
+static bool MakeSignedIntegerValue(
+        const std::string &typeName, const char *value, std::string *output) {
+    output->clear();
+
+    char *end;
+    long long x = strtoll(value, &end, 10);
+
+    if (end > value && *end == '\0' && errno != ERANGE) {
+        char out[32];
+        if (typeName == "byte") {
+            sprintf(out, "%d", (int)(int8_t)x);
+        } else if (typeName == "short") {
+            sprintf(out, "%d", (int)(int16_t)x);
+        } else if (typeName == "int") {
+            sprintf(out, "%d", (int)(int32_t)x);
+        } else {
+            assert(typeName == "long");
+            sprintf(out, "%lldL", (int64_t)x);
+        }
+
+        *output = out;
+        return true;
+    }
+
+    return false;
+}
+
+status_t EnumType::emitJavaTypeDeclarations(Formatter &out) const {
+    const ScalarType *scalarType = mStorageType->resolveToScalarType();
+    CHECK(scalarType != NULL);
+
+    out << "public final class "
+        << localName()
+        << " {\n";
+
+    out.indent();
+
+    const std::string typeName = scalarType->getJavaType();
+
+    std::vector<const EnumType *> chain;
+    const EnumType *type = this;
+    for (;;) {
+        chain.push_back(type);
+
+        const Type *superType = type->storageType();
+        if (superType == NULL || !superType->isEnum()) {
+            break;
+        }
+
+        type = static_cast<const EnumType *>(superType);
+    }
+
+    for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
+        const auto &type = *it;
+
+        for (const auto &entry : type->values()) {
+            out << "public static final "
+                << typeName
+                << " "
+                << entry->name();
+
+            const char *value = entry->value();
+            if (value != NULL) {
+                out << " = ";
+
+                std::string convertedValue;
+                if (MakeSignedIntegerValue(typeName, value, &convertedValue)) {
+                    out << convertedValue;
+                } else {
+                    // The value is not an integer, but some other string,
+                    // hopefully referring to some other enum name.
+                    out << value;
+                }
+            }
+
+            out << ";\n";
+        }
+    }
+
+    out.unindent();
+    out << "};\n\n";
+
+    return OK;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 EnumValue::EnumValue(const char *name, const char *value)
diff --git a/EnumType.h b/EnumType.h
index 939cd3b..6453166 100644
--- a/EnumType.h
+++ b/EnumType.h
@@ -23,6 +23,9 @@
 
     std::string getCppType(StorageMode mode, std::string *extra) const override;
 
+    std::string getJavaType() const override;
+    std::string getJavaSuffix() const override;
+
     void emitReaderWriter(
             Formatter &out,
             const std::string &name,
@@ -32,6 +35,7 @@
             ErrorMode mode) const override;
 
     status_t emitTypeDeclarations(Formatter &out) const override;
+    status_t emitJavaTypeDeclarations(Formatter &out) const override;
 
 private:
     std::vector<EnumValue *> *mValues;
diff --git a/FQName.cpp b/FQName.cpp
index 45e3ad4..dc35f70 100644
--- a/FQName.cpp
+++ b/FQName.cpp
@@ -186,6 +186,17 @@
     return out;
 }
 
+std::string FQName::javaPackage() const {
+    std::vector<std::string> components;
+    getPackageAndVersionComponents(&components, true /* cpp_compatible */);
+
+    return JoinStrings(components, ".");
+}
+
+std::string FQName::javaName() const {
+    return javaPackage() + "." + name();
+}
+
 void FQName::getPackageComponents(std::vector<std::string> *components) const {
     SplitString(package(), '.', components);
 }
diff --git a/FQName.h b/FQName.h
index 02c03bd..ae7fe80 100644
--- a/FQName.h
+++ b/FQName.h
@@ -36,13 +36,20 @@
     bool operator==(const FQName &other) const;
 
     // Returns an absolute C++ namespace prefix, i.e.
-    // ::android::hardware::Foo.
+    // ::android::hardware::Foo::V1_0.
     std::string cppNamespace() const;
 
     // Returns a fully qualified absolute C++ type name, i.e.
-    // ::android::hardware::Foo::bar::baz.
+    // ::android::hardware::Foo::V1_0::bar::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"
+    std::string javaName() const;
+
     void getPackageComponents(std::vector<std::string> *components) const;
 
     void getPackageAndVersionComponents(
diff --git a/Formatter.cpp b/Formatter.cpp
index c07b9df..060513c 100644
--- a/Formatter.cpp
+++ b/Formatter.cpp
@@ -69,13 +69,6 @@
 
 void Formatter::setNamespace(const std::string &space) {
     mSpace = space;
-    if (!mSpace.empty()) {
-        // The intent is for this to strip out all local namespace prefixes,
-        // so that a type "::android::hardware::Foo::bar::baz"
-        // is reduced to the equivalent "bar::baz" in the scope of the namespace
-        // "::android::hardware::Foo".
-        mSpace += "::";
-    }
 }
 
 void Formatter::output(const std::string &text) const {
diff --git a/HandleType.cpp b/HandleType.cpp
index 7ad2368..b799f0e 100644
--- a/HandleType.cpp
+++ b/HandleType.cpp
@@ -2,6 +2,8 @@
 
 #include "Formatter.h"
 
+#include <android-base/logging.h>
+
 namespace android {
 
 HandleType::HandleType() {}
@@ -12,6 +14,11 @@
     return "const ::native_handle_t*";
 }
 
+std::string HandleType::getJavaType() const {
+    CHECK(!"Should not be here");
+    return std::string();
+}
+
 void HandleType::emitReaderWriter(
         Formatter &out,
         const std::string &name,
diff --git a/HandleType.h b/HandleType.h
index 08dedb3..cab0cf8 100644
--- a/HandleType.h
+++ b/HandleType.h
@@ -10,6 +10,7 @@
     HandleType();
 
     std::string getCppType(StorageMode mode, std::string *extra) const override;
+    std::string getJavaType() const override;
 
     void emitReaderWriter(
             Formatter &out,
diff --git a/Interface.cpp b/Interface.cpp
index 203be93..ab117f2 100644
--- a/Interface.cpp
+++ b/Interface.cpp
@@ -47,6 +47,10 @@
     }
 }
 
+std::string Interface::getJavaType() const {
+    return fullJavaName();
+}
+
 void Interface::emitReaderWriter(
         Formatter &out,
         const std::string &name,
@@ -90,5 +94,25 @@
     }
 }
 
+void Interface::emitJavaReaderWriter(
+        Formatter &out,
+        const std::string &parcelObj,
+        const std::string &argName,
+        bool isReader) const {
+    if (isReader) {
+        out << fullJavaName()
+            << ".asInterface("
+            << parcelObj
+            << ".readStrongBinder());\n";
+    } else {
+        out << parcelObj
+            << ".writeStrongBinder("
+            << argName
+            << " == null ? null : "
+            << argName
+            << ".asBinder());\n";
+    }
+}
+
 }  // namespace android
 
diff --git a/Interface.h b/Interface.h
index d7bf0a3..a7d9afb 100644
--- a/Interface.h
+++ b/Interface.h
@@ -29,6 +29,8 @@
 
     std::string getCppType(StorageMode mode, std::string *extra) const override;
 
+    std::string getJavaType() const override;
+
     void emitReaderWriter(
             Formatter &out,
             const std::string &name,
@@ -37,6 +39,12 @@
             bool isReader,
             ErrorMode mode) const override;
 
+    void emitJavaReaderWriter(
+            Formatter &out,
+            const std::string &parcelObj,
+            const std::string &argName,
+            bool isReader) const override;
+
 private:
     Interface *mSuperType;
     std::vector<Method *> mMethods;
diff --git a/Method.cpp b/Method.cpp
index 2569701..8c140ff 100644
--- a/Method.cpp
+++ b/Method.cpp
@@ -53,6 +53,27 @@
     return out;
 }
 
+// static
+std::string Method::GetJavaSignature(const std::vector<TypedVar *> &args) {
+    bool first = true;
+    std::string out;
+    for (const auto &arg : args) {
+        if (!first) {
+            out += ", ";
+        }
+
+        std::string extra;
+        out += arg->type().getJavaType();
+        out += " ";
+        out += arg->name();
+        out += extra;
+
+        first = false;
+    }
+
+    return out;
+}
+
 void Method::dumpAnnotations(Formatter &out) const {
     if (mAnnotationsByName->size() == 0) {
         return;
diff --git a/Method.h b/Method.h
index 86c8f50..e449542 100644
--- a/Method.h
+++ b/Method.h
@@ -26,6 +26,7 @@
     const KeyedVector<std::string, Annotation *> &annotations() const;
 
     static std::string GetSignature(const std::vector<TypedVar *> &args);
+    static std::string GetJavaSignature(const std::vector<TypedVar *> &args);
 
     void dumpAnnotations(Formatter &out) const;
 
diff --git a/NamedType.cpp b/NamedType.cpp
index 77d2596..ed1ffe8 100644
--- a/NamedType.cpp
+++ b/NamedType.cpp
@@ -24,5 +24,9 @@
     return mFullName.cppName();
 }
 
+std::string NamedType::fullJavaName() const {
+    return mFullName.javaName();
+}
+
 }  // namespace android
 
diff --git a/NamedType.h b/NamedType.h
index e7e9605..d0d24ea 100644
--- a/NamedType.h
+++ b/NamedType.h
@@ -19,6 +19,7 @@
     const FQName &fqName() const;
     std::string localName() const;
     std::string fullName() const;
+    std::string fullJavaName() const;
 
 private:
     std::string mLocalName;
diff --git a/ScalarType.cpp b/ScalarType.cpp
index b15734b..bf111ca 100644
--- a/ScalarType.cpp
+++ b/ScalarType.cpp
@@ -39,6 +39,46 @@
     return kName[mKind];
 }
 
+std::string ScalarType::getJavaType() const {
+    static const char *const kName[] = {
+        "byte",
+        "boolean",
+        "long",
+        "byte",
+        "byte",
+        "short",
+        "short",
+        "int",
+        "int",
+        "long",
+        "long",
+        "float",
+        "double"
+    };
+
+    return kName[mKind];
+}
+
+std::string ScalarType::getJavaSuffix() const {
+    static const char *const kSuffix[] = {
+        "Int8",
+        "Int8",
+        "Pointer",
+        "Int8",
+        "Int8",
+        "Int16",
+        "Int16",
+        "Int32",
+        "Int32",
+        "Int64",
+        "Int64",
+        "Float",
+        "Double"
+    };
+
+    return kSuffix[mKind];
+}
+
 void ScalarType::emitReaderWriter(
         Formatter &out,
         const std::string &name,
diff --git a/ScalarType.h b/ScalarType.h
index 38b79d4..3e84597 100644
--- a/ScalarType.h
+++ b/ScalarType.h
@@ -31,6 +31,9 @@
 
     std::string getCppType(StorageMode mode, std::string *extra) const override;
 
+    std::string getJavaType() const override;
+    std::string getJavaSuffix() const override;
+
     void emitReaderWriter(
             Formatter &out,
             const std::string &name,
diff --git a/Scope.cpp b/Scope.cpp
index 5e8e687..ec865af 100644
--- a/Scope.cpp
+++ b/Scope.cpp
@@ -4,6 +4,8 @@
 #include "Formatter.h"
 #include "Interface.h"
 
+#include <android-base/logging.h>
+
 namespace android {
 
 Scope::Scope() {}
@@ -65,6 +67,11 @@
     return false;
 }
 
+std::string Scope::getJavaType() const {
+    CHECK(!"Should not be here");
+    return std::string();
+}
+
 status_t Scope::emitTypeDeclarations(Formatter &out) const {
     for (size_t i = 0; i < mTypes.size(); ++i) {
         status_t err = mTypes[i]->emitTypeDeclarations(out);
@@ -77,6 +84,18 @@
     return OK;
 }
 
+status_t Scope::emitJavaTypeDeclarations(Formatter &out) const {
+    for (size_t i = 0; i < mTypes.size(); ++i) {
+        status_t err = mTypes[i]->emitJavaTypeDeclarations(out);
+
+        if (err != OK) {
+            return err;
+        }
+    }
+
+    return OK;
+}
+
 status_t Scope::emitTypeDefinitions(
         Formatter &out, const std::string prefix) const {
     for (size_t i = 0; i < mTypes.size(); ++i) {
diff --git a/Scope.h b/Scope.h
index 90380e9..66b11f9 100644
--- a/Scope.h
+++ b/Scope.h
@@ -29,7 +29,10 @@
 
     bool containsSingleInterface(std::string *ifaceName) const;
 
+    std::string getJavaType() const override;
+
     status_t emitTypeDeclarations(Formatter &out) const override;
+    status_t emitJavaTypeDeclarations(Formatter &out) const override;
 
     status_t emitTypeDefinitions(
             Formatter &out, const std::string prefix) const override;
diff --git a/StringType.cpp b/StringType.cpp
index 5411e91..ca01687 100644
--- a/StringType.cpp
+++ b/StringType.cpp
@@ -23,6 +23,14 @@
     }
 }
 
+std::string StringType::getJavaType() const {
+    return "String";
+}
+
+std::string StringType::getJavaSuffix() const {
+    return "String";
+}
+
 void StringType::emitReaderWriter(
         Formatter &out,
         const std::string &name,
diff --git a/StringType.h b/StringType.h
index e7e00d4..0fc6223 100644
--- a/StringType.h
+++ b/StringType.h
@@ -11,6 +11,9 @@
 
     std::string getCppType(StorageMode mode, std::string *extra) const override;
 
+    std::string getJavaType() const override;
+    std::string getJavaSuffix() const override;
+
     void emitReaderWriter(
             Formatter &out,
             const std::string &name,
diff --git a/Type.cpp b/Type.cpp
index bec4655..1e7e2d5 100644
--- a/Type.cpp
+++ b/Type.cpp
@@ -45,6 +45,11 @@
     return std::string();
 }
 
+std::string Type::getJavaSuffix() const {
+    CHECK(!"Should not be here");
+    return std::string();
+}
+
 void Type::emitReaderWriter(
         Formatter &,
         const std::string &,
@@ -68,6 +73,20 @@
     CHECK(!"Should not be here");
 }
 
+void Type::emitJavaReaderWriter(
+        Formatter &out,
+        const std::string &parcelObj,
+        const std::string &argName,
+        bool isReader) const {
+    emitJavaReaderWriterWithSuffix(
+            out,
+            parcelObj,
+            argName,
+            isReader,
+            getJavaSuffix(),
+            "" /* extra */);
+}
+
 void Type::handleError(Formatter &out, ErrorMode mode) const {
     switch (mode) {
         case ErrorMode_Ignore:
@@ -189,6 +208,10 @@
     return OK;
 }
 
+status_t Type::emitJavaTypeDeclarations(Formatter &) const {
+    return OK;
+}
+
 bool Type::needsEmbeddedReadWrite() const {
     return false;
 }
@@ -209,5 +232,28 @@
     return getCppType(StorageMode_Argument, extra);
 }
 
+void Type::emitJavaReaderWriterWithSuffix(
+        Formatter &out,
+        const std::string &parcelObj,
+        const std::string &argName,
+        bool isReader,
+        const std::string &suffix,
+        const std::string &extra) const {
+    out << parcelObj
+        << "."
+        << (isReader ? "read" : "write")
+        << suffix
+        << "(";
+
+    if (isReader) {
+        out << extra;
+    } else {
+        out << (extra.empty() ? "" : (extra + ", "));
+        out << argName;
+    }
+
+    out << ");\n";
+}
+
 }  // namespace android
 
diff --git a/Type.h b/Type.h
index 03c6615..c24b907 100644
--- a/Type.h
+++ b/Type.h
@@ -40,6 +40,9 @@
     std::string getCppResultType(std::string *extra) const;
     std::string getCppArgumentType(std::string *extra) const;
 
+    virtual std::string getJavaType() const = 0;
+    virtual std::string getJavaSuffix() const;
+
     enum ErrorMode {
         ErrorMode_Ignore,
         ErrorMode_Goto,
@@ -65,11 +68,19 @@
             const std::string &parentName,
             const std::string &offsetText) const;
 
+    virtual void emitJavaReaderWriter(
+            Formatter &out,
+            const std::string &parcelObj,
+            const std::string &argName,
+            bool isReader) const;
+
     virtual status_t emitTypeDeclarations(Formatter &out) const;
 
     virtual status_t emitTypeDefinitions(
             Formatter &out, const std::string prefix) const;
 
+    virtual status_t emitJavaTypeDeclarations(Formatter &out) const;
+
     virtual bool needsEmbeddedReadWrite() const;
     virtual bool resultNeedsDeref() const;
 
@@ -90,6 +101,14 @@
             const std::string &typeName,
             const std::string &childName) const;
 
+    void emitJavaReaderWriterWithSuffix(
+            Formatter &out,
+            const std::string &parcelObj,
+            const std::string &argName,
+            bool isReader,
+            const std::string &suffix,
+            const std::string &extra) const;
+
 private:
     DISALLOW_COPY_AND_ASSIGN(Type);
 };
diff --git a/TypeDef.cpp b/TypeDef.cpp
index 1e3c5b7..bbdacd7 100644
--- a/TypeDef.cpp
+++ b/TypeDef.cpp
@@ -34,6 +34,11 @@
     return true;
 }
 
+std::string TypeDef::getJavaType() const {
+    CHECK(!"Should not be here");
+    return std::string();
+}
+
 bool TypeDef::needsEmbeddedReadWrite() const {
     CHECK(!"Should not be here");
     return false;
diff --git a/TypeDef.h b/TypeDef.h
index f3030bf..23d939d 100644
--- a/TypeDef.h
+++ b/TypeDef.h
@@ -17,6 +17,8 @@
     bool isEnum() const override;
     bool isTypeDef() const override;
 
+    std::string getJavaType() const override;
+
     bool needsEmbeddedReadWrite() const override;
     bool resultNeedsDeref() const override;
 
diff --git a/VectorType.cpp b/VectorType.cpp
index fcfda4b..fc2360e 100644
--- a/VectorType.cpp
+++ b/VectorType.cpp
@@ -30,6 +30,14 @@
     }
 }
 
+std::string VectorType::getJavaType() const {
+    return mElementType->getJavaType() + "[]";
+}
+
+std::string VectorType::getJavaSuffix() const {
+    return mElementType->getJavaSuffix() + "Vector";
+}
+
 void VectorType::emitReaderWriter(
         Formatter &out,
         const std::string &name,
diff --git a/VectorType.h b/VectorType.h
index f0db95c..905214b 100644
--- a/VectorType.h
+++ b/VectorType.h
@@ -11,6 +11,9 @@
 
     std::string getCppType(StorageMode mode, std::string *extra) const override;
 
+    std::string getJavaType() const override;
+    std::string getJavaSuffix() const override;
+
     void emitReaderWriter(
             Formatter &out,
             const std::string &name,
diff --git a/generateCpp.cpp b/generateCpp.cpp
index a8f95a6..51e8143 100644
--- a/generateCpp.cpp
+++ b/generateCpp.cpp
@@ -79,7 +79,7 @@
             out << "namespace " << component << " {\n";
         }
 
-        out.setNamespace(mPackage.cppNamespace());
+        out.setNamespace(mPackage.cppNamespace() + "::");
     } else {
         out.setNamespace(std::string());
 
diff --git a/generateJava.cpp b/generateJava.cpp
new file mode 100644
index 0000000..2b0fbf8
--- /dev/null
+++ b/generateJava.cpp
@@ -0,0 +1,498 @@
+#include "AST.h"
+
+#include "Coordinator.h"
+#include "Formatter.h"
+#include "Interface.h"
+#include "Method.h"
+#include "Scope.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+
+static std::string upcase(const std::string in) {
+    std::string out{in};
+
+    for (auto &ch : out) {
+        ch = toupper(ch);
+    }
+
+    return out;
+}
+
+void AST::emitJavaReaderWriter(
+        Formatter &out,
+        const std::string &parcelObj,
+        const TypedVar *arg,
+        bool isReader) const {
+    if (isReader) {
+        out << arg->type().getJavaType()
+            << " "
+            << arg->name()
+            << " = ";
+    }
+
+    arg->type().emitJavaReaderWriter(out, parcelObj, arg->name(), isReader);
+}
+
+status_t AST::generateJava(const std::string &outputPath) const {
+    std::string ifaceName;
+    if (!AST::isInterface(&ifaceName)) {
+        fprintf(stderr, "Common types are unsupported in the Java backend.\n");
+        return UNKNOWN_ERROR;
+    }
+
+    // cut off the leading 'I'.
+    const std::string baseName = ifaceName.substr(1);
+
+    std::string path = outputPath;
+    path.append(mCoordinator->convertPackageRootToPath(mPackage));
+    path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
+    path.append(ifaceName);
+    path.append(".java");
+
+    CHECK(Coordinator::MakeParentHierarchy(path));
+    FILE *file = fopen(path.c_str(), "w");
+
+    if (file == NULL) {
+        return -errno;
+    }
+
+    Formatter out(file);
+
+    std::vector<std::string> packageComponents;
+    getPackageAndVersionComponents(
+            &packageComponents, true /* cpp_compatible */);
+
+    out << "package " << mPackage.javaPackage() << ";\n\n";
+
+    out << "import android.os.IHwBinder;\n";
+    out << "import android.os.IHwInterface;\n";
+    out << "import android.os.HwBinder;\n";
+    out << "import android.os.HwParcel;\n\n";
+
+    for (const auto &item : mImportedNames) {
+        out << "import " << item.javaName() << ";\n";
+    }
+
+    if (!mImportedNames.empty()) {
+        out << "\n";
+    }
+
+    out.setNamespace(mPackage.javaPackage() + ".");
+
+    const Interface *iface = mRootScope->getInterface();
+    const Interface *superType = iface->superType();
+
+    out << "public interface " << ifaceName << " extends ";
+
+    if (superType != NULL) {
+        out << superType->fullJavaName();
+    } else {
+        out << "IHwInterface";
+    }
+
+    out << " {\n";
+    out.indent();
+
+    out << "public static final String kInterfaceName = \""
+        << mPackage.string()
+        << "::"
+        << ifaceName
+        << "\";\n\n";
+
+    out << "public static "
+        << ifaceName
+        << " asInterface(IHwBinder binder) {\n";
+
+    out.indent();
+
+    out << "if (binder == null) {\n";
+    out.indent();
+    out << "return null;\n";
+    out.unindent();
+    out << "}\n\n";
+
+    out << "IHwInterface iface =\n";
+    out.indent();
+    out.indent();
+    out << "binder.queryLocalInterface(kInterfaceName);\n\n";
+    out.unindent();
+    out.unindent();
+
+    out << "if ((iface != null) && (iface instanceof "
+        << ifaceName
+        << ")) {\n";
+
+    out.indent();
+    out << "return (" << ifaceName << ")iface;\n";
+    out.unindent();
+    out << "}\n\n";
+
+    out << "return new " << ifaceName << ".Proxy(binder);\n";
+
+    out.unindent();
+    out << "}\n\n";
+
+    out << "public IHwBinder asBinder();\n\n";
+
+    status_t err = emitJavaTypeDeclarations(out);
+
+    if (err != OK) {
+        return err;
+    }
+
+    const std::string base = (superType != NULL)
+        ? (superType->fullJavaName() + ".kOpEnd")
+        : "IHwBinder.FIRST_CALL_TRANSACTION";
+
+    bool first = true;
+    size_t index = 0;
+    for (const auto &method : iface->methods()) {
+        out << "public static final int kOp_"
+            << upcase(method->name())
+            << " = "
+            << base;
+
+        if (!first) {
+            out << " + " << index;
+        }
+
+        out << ";\n";
+
+        ++index;
+        first = false;
+    }
+
+    out << "public static final int kOpEnd = "
+        << base
+        << " + "
+        << index
+        << ";";
+
+    out << "\n\n";
+
+    for (const auto &method : iface->methods()) {
+        const bool returnsValue = !method->results().empty();
+        const bool needsCallback = method->results().size() > 1;
+
+        if (needsCallback) {
+            out << "\npublic abstract class "
+                << method->name()
+                << "Callback {\n";
+
+            out.indent();
+
+            out << "public abstract void onValues("
+                << Method::GetJavaSignature(method->results())
+                << ");\n";
+
+            out.unindent();
+            out << "}\n\n";
+        }
+
+        if (returnsValue && !needsCallback) {
+            out << method->results()[0]->type().getJavaType();
+        } else {
+            out << "void";
+        }
+
+        out << " "
+            << method->name()
+            << "("
+            << Method::GetJavaSignature(method->args());
+
+        if (needsCallback) {
+            if (!method->args().empty()) {
+                out << ", ";
+            }
+
+            out << method->name()
+                << "Callback cb";
+        }
+
+        out << ");\n";
+    }
+
+    out << "\npublic static final class Proxy implements "
+        << ifaceName
+        << " {\n";
+
+    out.indent();
+
+    out << "private IHwBinder mRemote;\n\n";
+    out << "public Proxy(IHwBinder remote) {\n";
+    out.indent();
+    out << "mRemote = remote;\n";
+    out.unindent();
+    out << "}\n\n";
+
+    out << "public IHwBinder asBinder() {\n";
+    out.indent();
+    out << "return mRemote;\n";
+    out.unindent();
+    out << "}\n\n";
+
+    std::vector<const Interface *> chain;
+    while (iface != NULL) {
+        chain.push_back(iface);
+        iface = iface->superType();
+    }
+
+    for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
+        const Interface *superInterface = *it;
+
+        out << "// Methods from "
+            << superInterface->fullName()
+            << " follow.\n";
+
+        for (const auto &method : superInterface->methods()) {
+            const bool returnsValue = !method->results().empty();
+            const bool needsCallback = method->results().size() > 1;
+
+            out << "public ";
+            if (returnsValue && !needsCallback) {
+                out << method->results()[0]->type().getJavaType();
+            } else {
+                out << "void";
+            }
+
+            out << " "
+                << method->name()
+                << "("
+                << Method::GetJavaSignature(method->args());
+
+            if (needsCallback) {
+                if (!method->args().empty()) {
+                    out << ", ";
+                }
+
+                out << method->name()
+                    << "Callback cb";
+            }
+
+            out << ") {\n";
+            out.indent();
+
+            out << "HwParcel request = new HwParcel();\n";
+            out << "request.writeInterfaceToken("
+                << superInterface->fullJavaName()
+                << ".kInterfaceName);\n";
+
+            for (const auto &arg : method->args()) {
+                emitJavaReaderWriter(
+                        out,
+                        "request",
+                        arg,
+                        false /* isReader */);
+            }
+
+            out << "\nHwParcel reply = new HwParcel();\n"
+               << "mRemote.transact(kOp_"
+               << upcase(method->name())
+               << ", request, reply, 0 /* flags */);\n"
+               << "reply.verifySuccess();\n"
+               << "request.releaseTemporaryStorage();\n";
+
+            if (returnsValue) {
+                out << "\n";
+
+                for (const auto &arg : method->results()) {
+                    emitJavaReaderWriter(
+                            out,
+                            "reply",
+                            arg,
+                            true /* isReader */);
+                }
+
+                if (needsCallback) {
+                    out << "cb.onValues(";
+
+                    bool firstField = true;
+                    for (const auto &arg : method->results()) {
+                        if (!firstField) {
+                            out << ", ";
+                        }
+
+                        out << arg->name();
+                        firstField = false;
+                    }
+
+                    out << ");\n";
+                } else {
+                    const std::string returnName = method->results()[0]->name();
+                    out << "return " << returnName << ";\n";
+                }
+            }
+
+            out.unindent();
+            out << "}\n\n";
+        }
+    }
+
+    out.unindent();
+    out << "}\n";
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    out << "\npublic static abstract class Stub extends HwBinder "
+        << "implements "
+        << ifaceName << " {\n";
+
+    out.indent();
+
+    out << "public IHwBinder asBinder() {\n";
+    out.indent();
+    out << "return this;\n";
+    out.unindent();
+    out << "}\n\n";
+
+    out << "public IHwInterface queryLocalInterface(String descriptor) {\n";
+    out.indent();
+    // XXX what about potential superClasses?
+    out << "if (kInterfaceName.equals(descriptor)) {\n";
+    out.indent();
+    out << "return this;\n";
+    out.unindent();
+    out << "}\n";
+    out << "return null;\n";
+    out.unindent();
+    out << "}\n\n";
+
+    out << "public void onTransact("
+        << "int code, HwParcel request, final HwParcel reply, "
+        << "int flags) {\n";
+
+    out.indent();
+
+    out << "switch (code) {\n";
+
+    out.indent();
+
+    for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
+        const Interface *superInterface = *it;
+
+        for (const auto &method : superInterface->methods()) {
+            const bool returnsValue = !method->results().empty();
+            const bool needsCallback = method->results().size() > 1;
+
+            out << "case "
+                << superInterface->fullJavaName()
+                << ".kOp_"
+                <<
+                upcase(method->name())
+                << ":\n{\n";
+
+            out.indent();
+
+            out << "request.enforceInterface("
+                << superInterface->fullJavaName()
+                << ".kInterfaceName);\n\n";
+
+            for (const auto &arg : method->args()) {
+                emitJavaReaderWriter(
+                        out,
+                        "request",
+                        arg,
+                        true /* isReader */);
+            }
+
+            if (!needsCallback && returnsValue) {
+                const TypedVar *returnArg = method->results()[0];
+
+                out << returnArg->type().getJavaType()
+                    << " "
+                    << returnArg->name()
+                    << " = ";
+            }
+
+            out << method->name()
+                << "(";
+
+            bool firstField = true;
+            for (const auto &arg : method->args()) {
+                if (!firstField) {
+                    out << ", ";
+                }
+
+                out << arg->name();
+
+                firstField = false;
+            }
+
+            if (needsCallback) {
+                if (!firstField) {
+                    out << ", ";
+                }
+
+                out << "new " << method->name() << "Callback() {\n";
+                out.indent();
+
+                out << "@Override\n"
+                    << "public void onValues("
+                    << Method::GetJavaSignature(method->results())
+                    << ") {\n";
+
+                out.indent();
+                out << "reply.writeStatus(HwParcel.STATUS_SUCCESS);\n";
+
+                for (const auto &arg : method->results()) {
+                    emitJavaReaderWriter(
+                            out,
+                            "reply",
+                            arg,
+                            false /* isReader */);
+                }
+
+                out << "reply.send();\n"
+                          << "}}";
+
+                out.unindent();
+                out.unindent();
+            }
+
+            out << ");\n";
+
+            if (!needsCallback) {
+                out << "reply.writeStatus(HwParcel.STATUS_SUCCESS);\n";
+
+                if (returnsValue) {
+                    const TypedVar *returnArg = method->results()[0];
+
+                    emitJavaReaderWriter(
+                            out,
+                            "reply",
+                            returnArg,
+                            false /* isReader */);
+                }
+
+                out << "reply.send();\n";
+            }
+
+            out << "break;\n";
+            out.unindent();
+            out << "}\n\n";
+        }
+    }
+
+    out.unindent();
+    out << "}\n";
+
+    out.unindent();
+    out << "}\n";
+
+    out.unindent();
+    out << "}\n";
+
+    out.unindent();
+    out << "}\n";
+
+    return OK;
+}
+
+status_t AST::emitJavaTypeDeclarations(Formatter &out) const {
+    return mRootScope->emitJavaTypeDeclarations(out);
+}
+
+}  // namespace android
+
diff --git a/main.cpp b/main.cpp
index 752e5f8..9b0bfab 100644
--- a/main.cpp
+++ b/main.cpp
@@ -31,7 +31,8 @@
         const FQName &fqName,
         const char *,
         Coordinator *coordinator,
-        const std::string &outputDir) {
+        const std::string &outputDir,
+        bool java = false) {
 
     CHECK(fqName.isFullyQualified());
 
@@ -45,7 +46,8 @@
         return UNKNOWN_ERROR;
     }
 
-    status_t err = ast->generateCpp(outputDir);
+    status_t err =
+        java ? ast->generateJava(outputDir) : ast->generateCpp(outputDir);
 
     return err;
 }
@@ -54,7 +56,8 @@
         const FQName &packageFQName,
         const char *hidl_gen,
         Coordinator *coordinator,
-        const std::string &outputDir) {
+        const std::string &outputDir,
+        bool java = false) {
 
     CHECK(packageFQName.isValid() &&
           !packageFQName.isFullyQualified() &&
@@ -71,7 +74,9 @@
     }
 
     for (const auto &fqName : packageInterfaces) {
-        err = generateSourcesForFile(fqName, hidl_gen, coordinator, outputDir);
+        err = generateSourcesForFile(
+                fqName, hidl_gen, coordinator, outputDir, java);
+
         if (err != OK) {
             return err;
         }
@@ -222,7 +227,7 @@
     return OutputHandler::PASS_PACKAGE;
 }
 
-OutputHandler::ValRes validateForCpp(const FQName &fqName) {
+OutputHandler::ValRes validateForCppOrJava(const FQName &fqName) {
     if (fqName.package().empty()) {
         fprintf(stderr, "Expecting package name\n");
         return OutputHandler::FAILED;
@@ -240,8 +245,8 @@
 
 static std::vector<OutputHandler> formats = {
     {"c++",
-     true,
-     validateForCpp,
+     true /* mNeedsOutputDir */,
+     validateForCppOrJava,
      [](const FQName &fqName,
         const char *hidl_gen, Coordinator *coordinator,
         const std::string &outputDir) -> status_t {
@@ -260,8 +265,31 @@
         }
     },
 
+    {"java",
+     true /* mNeedsOutputDir */,
+     validateForCppOrJava,
+     [](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,
+                                              true /* java */);
+            }
+            else {
+                return generateSourcesForPackage(fqName,
+                                                 hidl_gen,
+                                                 coordinator,
+                                                 outputDir,
+                                                 true /* java */);
+            }
+        }
+    },
+
     {"makefile",
-     false,
+     false /* mNeedsOutputDir */,
      validateForMakefile,
      generateMakefileForPackage,
     },
diff --git a/test/Android.mk b/test/Android.mk
index 240f3ed..b753dd6 100644
--- a/test/Android.mk
+++ b/test/Android.mk
@@ -7,18 +7,19 @@
 LOCAL_SRC_FILES :=                                  \
     main.cpp                                        \
 
-LOCAL_SHARED_LIBRARIES :=             \
-    libandroid_runtime                \
-    libbase                           \
-    libcutils                         \
-    libhwbinder                       \
-    libnativehelper                   \
-    libutils                          \
-    android.hardware.tests.foo@1.0    \
-    android.hardware.tests.bar@1.0
+LOCAL_SHARED_LIBRARIES :=       \
+    libandroid_runtime          \
+    libbase                     \
+    libcutils                   \
+    libhwbinder                 \
+    libnativehelper             \
+    libutils                    \
+    android.hardware.foo@1.0    \
+    android.hardware.bar@1.0    \
+    android.hardware.nfc@1.0    \
 
 LOCAL_MODULE_TAGS := samples
 
 LOCAL_CFLAGS := -O0 -g
 
-include $(BUILD_EXECUTABLE)
+# include $(BUILD_EXECUTABLE)