Merge "GTest test-cases for HIDL safe unions in Java"
diff --git a/AST.cpp b/AST.cpp
index 81f8c0b..de69965 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -83,8 +83,8 @@
     return mRootScope.getInterface() != nullptr;
 }
 
-bool AST::containsInterfaces() const {
-    return mRootScope.containsInterfaces();
+bool AST::definesInterfaces() const {
+    return mRootScope.definesInterfaces();
 }
 
 status_t AST::postParse() {
diff --git a/AST.h b/AST.h
index 4de98bd..3e8e5a5 100644
--- a/AST.h
+++ b/AST.h
@@ -53,7 +53,7 @@
     // package and version really.
     FQName package() const;
     bool isInterface() const;
-    bool containsInterfaces() const;
+    bool definesInterfaces() const;
 
     // Adds package, version and scope stack to local name
     FQName makeFullName(const char* localName, Scope* scope) const;
diff --git a/ArrayType.cpp b/ArrayType.cpp
index 5155a27..2d672df 100644
--- a/ArrayType.cpp
+++ b/ArrayType.cpp
@@ -156,10 +156,6 @@
     return base;
 }
 
-std::string ArrayType::getJavaWrapperType() const {
-    return mElementType->getJavaWrapperType();
-}
-
 std::string ArrayType::getVtsType() const {
     return "TYPE_ARRAY";
 }
@@ -366,7 +362,8 @@
     out << streamName << ".append(java.util.Arrays."
         << (countDimensions() > 1 ? "deepToString" : "toString")
         << "("
-        << name << "));\n";
+        << name
+        << "));\n";
 }
 
 
@@ -434,15 +431,17 @@
 
 void ArrayType::emitJavaFieldInitializer(
         Formatter &out, const std::string &fieldName) const {
-    std::string typeName = getJavaType(false /* forInitializer */);
-    std::string initName = getJavaType(true /* forInitializer */);
+    const std::string typeName = getJavaType(false /* forInitializer */);
+    const std::string fieldDeclaration = "final " + typeName + " " + fieldName;
 
-    out << "final "
-        << typeName
-        << " "
-        << fieldName
+    emitJavaFieldDefaultInitialValue(out, fieldDeclaration);
+}
+
+void ArrayType::emitJavaFieldDefaultInitialValue(
+        Formatter &out, const std::string &declaredFieldName) const {
+    out << declaredFieldName
         << " = new "
-        << initName
+        << getJavaType(true /* forInitializer */)
         << ";\n";
 }
 
@@ -488,15 +487,13 @@
         indexString += "[" + iteratorName + "]";
     }
 
-    if (isReader && mElementType->isCompoundType()) {
-        std::string typeName =
-            mElementType->getJavaType(false /* forInitializer */);
+    const bool isIndexed = (loopDimensions > 0);
+    const std::string fieldNameWithCast = isIndexed
+            ? "(" + getJavaTypeCast(fieldName) + ")" + indexString
+            : getJavaTypeCast(fieldName);
 
-        out << fieldName
-            << indexString
-            << " = new "
-            << typeName
-            << "();\n";
+    if (isReader && mElementType->isCompoundType()) {
+        mElementType->emitJavaFieldDefaultInitialValue(out, fieldNameWithCast);
     }
 
     if (!isPrimitiveArray) {
@@ -505,7 +502,7 @@
                 depth + 1,
                 parcelName,
                 blobName,
-                fieldName + indexString,
+                fieldNameWithCast,
                 offsetName,
                 isReader);
 
@@ -521,8 +518,7 @@
                 << "Array("
                 << offsetName
                 << ", "
-                << fieldName
-                << indexString
+                << fieldNameWithCast
                 << ", "
                 << mSizes.back()->javaValue()
                 << " /* size */);\n";
@@ -533,8 +529,7 @@
                 << "Array("
                 << offsetName
                 << ", "
-                << fieldName
-                << indexString
+                << fieldNameWithCast
                 << ");\n";
         }
 
diff --git a/ArrayType.h b/ArrayType.h
index 2c13a33..2ced071 100644
--- a/ArrayType.h
+++ b/ArrayType.h
@@ -56,8 +56,6 @@
 
     std::string getJavaType(bool forInitializer) const override;
 
-    std::string getJavaWrapperType() const override;
-
     std::string getVtsType() const override;
 
     void emitReaderWriter(
@@ -121,6 +119,9 @@
     void emitJavaFieldInitializer(
             Formatter &out, const std::string &fieldName) const override;
 
+    void emitJavaFieldDefaultInitialValue(
+            Formatter &out, const std::string &declaredFieldName) const override;
+
     void emitJavaFieldReaderWriter(
             Formatter &out,
             size_t depth,
diff --git a/CompoundType.cpp b/CompoundType.cpp
index db7c75a..178b132 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -50,12 +50,18 @@
         const Type& type = field->type();
 
         if ((type.isVector() && static_cast<const VectorType*>(&type)->isVectorOfBinders())) {
-            std::cerr << "ERROR: Struct/Union must not contain references to interfaces at "
+            std::cerr << "ERROR: Struct/union cannot contain vectors of interfaces at "
                       << field->location() << "\n";
             return UNKNOWN_ERROR;
         }
 
         if (mStyle == STYLE_UNION) {
+            if (type.isBinder()) {
+                std::cerr << "ERROR: Union cannot contain interfaces at " << field->location()
+                          << "\n";
+                return UNKNOWN_ERROR;
+            }
+
             if (type.needsEmbeddedReadWrite()) {
                 std::cerr << "ERROR: Union must not contain any types that need fixup at "
                           << field->location() << "\n";
@@ -392,10 +398,13 @@
 
 void CompoundType::emitJavaFieldInitializer(
         Formatter &out, const std::string &fieldName) const {
-    out << "final "
-        << fullJavaName()
-        << " "
-        << fieldName
+    const std::string fieldDeclaration = "final " + fullJavaName() + " " + fieldName;
+    emitJavaFieldDefaultInitialValue(out, fieldDeclaration);
+}
+
+void CompoundType::emitJavaFieldDefaultInitialValue(
+        Formatter &out, const std::string &declaredFieldName) const {
+    out << declaredFieldName
         << " = new "
         << fullJavaName()
         << "();\n";
@@ -410,8 +419,9 @@
         const std::string &offset,
         bool isReader) const {
     if (isReader) {
-        out << fieldName
-            << ".readEmbeddedFromParcel("
+        out << "("
+            << getJavaTypeCast(fieldName)
+            << ").readEmbeddedFromParcel("
             << parcelName
             << ", "
             << blobName
@@ -977,7 +987,7 @@
         << "& other) ";
 
     out.block([&] {
-        out << "switch(other.hidl_d) ";
+        out << "switch (other.hidl_d) ";
         out.block([&] {
 
             for (const auto& field : *mFields) {
@@ -1015,7 +1025,7 @@
         << "::hidl_destructUnion() ";
 
     out.block([&] {
-        out << "switch(hidl_d) ";
+        out << "switch (hidl_d) ";
         out.block([&] {
 
             for (const auto& field : *mFields) {
@@ -1181,7 +1191,6 @@
     }
 }
 
-// TODO(b/79878527): Implement Java bindings for safe unions
 void CompoundType::emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const {
     out << "public final ";
 
@@ -1197,15 +1206,128 @@
 
     Scope::emitJavaTypeDeclarations(out, false /* atTopLevel */);
 
-    for (const auto& field : *mFields) {
-        field->emitDocComment(out);
+    if (mStyle == STYLE_SAFE_UNION && !mFields->empty()) {
+        const std::string discriminatorStorageType = (
+                getUnionDiscriminatorType()->getJavaType(false));
 
-        out << "public ";
+        out << "public static final class hidl_discriminator ";
+        out.block([&] {
+            std::vector<std::string> enumNames;
+            for (const auto& field : *mFields) {
+                enumNames.push_back(field->name());
+            }
+            enumNames.push_back("hidl_no_init");
 
-        field->type().emitJavaFieldInitializer(out, field->name());
-    }
+            for (size_t idx = 0; idx < enumNames.size(); idx++) {
+                out << "public static final "
+                    << discriminatorStorageType
+                    << " "
+                    << enumNames[idx]
+                    << " = "
+                    << idx
+                    << ";\n";
+            }
 
-    if (!mFields->empty()) {
+            out << "\n"
+                << "public static final String getName("
+                << discriminatorStorageType
+                << " value) ";
+
+            out.block([&] {
+                out << "switch (value) ";
+                out.block([&] {
+                    for (size_t idx = 0; idx < enumNames.size(); idx++) {
+                        out << "case "
+                            << idx
+                            << ": { return \""
+                            << enumNames[idx]
+                            << "\"; }\n";
+                    }
+                    out << "default: { return \"Unknown\"; }\n";
+                }).endl();
+            }).endl().endl();
+
+            out << "private hidl_discriminator() {}\n";
+        }).endl().endl();
+
+        out << "private "
+            << discriminatorStorageType
+            << " hidl_d = hidl_discriminator.hidl_no_init;\n";
+
+        out << "private Object hidl_o;\n\n";
+
+        for (const auto& field : *mFields) {
+            // Setter
+            out << "public void "
+                << field->name()
+                << "("
+                << field->type().getJavaType(false)
+                << " "
+                << field->name()
+                << ") ";
+
+            out.block([&] {
+                out << "hidl_d = hidl_discriminator."
+                    << field->name()
+                    << ";\n";
+
+                out << "hidl_o = "
+                    << field->name()
+                    << ";\n";
+            }).endl().endl();
+
+            // Getter
+            out << "public "
+                << field->type().getJavaType(false)
+                << " "
+                << field->name()
+                << "() ";
+
+            out.block([&] {
+                out << "if (hidl_d != hidl_discriminator."
+                    << field->name()
+                    << ") ";
+
+                out.block([&] {
+                    out << "String className = (hidl_o != null) ? "
+                        << "hidl_o.getClass().getName() : \"null\";\n";
+
+                    out << "throw new IllegalStateException(\n";
+                    out.indent(2, [&] {
+                        out << "\"Read access to inactive union components is disallowed. \" +\n"
+                            << "\"Discriminator value is \" + hidl_d + \" (corresponding \" +\n"
+                            << "\"to \" + hidl_discriminator.getName(hidl_d) + \"), and \" +\n"
+                            << "\"hidl_o is of type \" + className + \".\");\n";
+                    });
+                }).endl();
+
+                out << "if (hidl_o != null && !"
+                    << field->type().getJavaTypeClass()
+                    << ".class.isInstance(hidl_o)) ";
+
+                out.block([&] {
+                    out << "throw new Error(\"Union is in a corrupted state.\");\n";
+                }).endl();
+
+                out << "return ("
+                    << field->type().getJavaTypeCast("hidl_o")
+                    << ");\n";
+            }).endl().endl();
+        }
+
+        out << "// Utility method\n"
+            << "public "
+            << discriminatorStorageType
+            << " getDiscriminator() { return hidl_d; }\n\n";
+
+    } else if (!mFields->empty()) {
+        for (const auto& field : *mFields) {
+            field->emitDocComment(out);
+
+            out << "public ";
+            field->type().emitJavaFieldInitializer(out, field->name());
+        }
+
         out << "\n";
     }
 
@@ -1225,14 +1347,27 @@
                 out << "return false;\n";
             }).endl();
             out << fullJavaName() << " other = (" << fullJavaName() << ")otherObject;\n";
-            for (const auto &field : *mFields) {
-                std::string condition = (field->type().isScalar() || field->type().isEnum())
-                    ? "this." + field->name() + " != other." + field->name()
-                    : ("!android.os.HidlSupport.deepEquals(this." + field->name()
-                            + ", other." + field->name() + ")");
-                out.sIf(condition, [&] {
+
+            if (mStyle == STYLE_SAFE_UNION && !mFields->empty()) {
+                out.sIf("this.hidl_d != other.hidl_d", [&] {
                     out << "return false;\n";
                 }).endl();
+                out.sIf("this.hidl_d == hidl_discriminator.hidl_no_init", [&] {
+                    out << "return false;\n";
+                }).endl();
+                out.sIf("!android.os.HidlSupport.deepEquals(this.hidl_o, other.hidl_o)", [&] {
+                    out << "return false;\n";
+                }).endl();
+            } else if (!mFields->empty()) {
+                for (const auto &field : *mFields) {
+                    std::string condition = (field->type().isScalar() || field->type().isEnum())
+                        ? "this." + field->name() + " != other." + field->name()
+                        : ("!android.os.HidlSupport.deepEquals(this." + field->name()
+                                + ", other." + field->name() + ")");
+                    out.sIf(condition, [&] {
+                        out << "return false;\n";
+                    }).endl();
+                }
             }
             out << "return true;\n";
         }).endl().endl();
@@ -1241,9 +1376,14 @@
         out.block([&] {
             out << "return java.util.Objects.hash(\n";
             out.indent(2, [&] {
-                out.join(mFields->begin(), mFields->end(), ", \n", [&] (const auto &field) {
-                    out << "android.os.HidlSupport.deepHashCode(this." << field->name() << ")";
-                });
+                if (mStyle == STYLE_SAFE_UNION && !mFields->empty()) {
+                    out << "android.os.HidlSupport.deepHashCode(this.hidl_o),\n"
+                        << "java.util.Objects.hashCode(this.hidl_d)";
+                } else {
+                    out.join(mFields->begin(), mFields->end(), ", \n", [&] (const auto &field) {
+                        out << "android.os.HidlSupport.deepHashCode(this." << field->name() << ")";
+                    });
+                }
             });
             out << ");\n";
         }).endl().endl();
@@ -1257,32 +1397,94 @@
     out.block([&] {
         out << "java.lang.StringBuilder builder = new java.lang.StringBuilder();\n"
             << "builder.append(\"{\");\n";
-        for (const auto &field : *mFields) {
-            out << "builder.append(\"";
-            if (field != *(mFields->begin())) {
-                out << ", ";
-            }
-            out << "." << field->name() << " = \");\n";
-            field->type().emitJavaDump(out, "builder", "this." + field->name());
+
+        if (mStyle == STYLE_SAFE_UNION && !mFields->empty()) {
+            out << "switch (this.hidl_d) {\n";
+            out.indent();
         }
+
+        for (const auto &field : *mFields) {
+            if (mStyle == STYLE_SAFE_UNION) {
+                out << "case hidl_discriminator."
+                    << field->name()
+                    << ": ";
+
+                out.block([&] {
+                    out << "builder.append(\""
+                        << "."
+                        << field->name()
+                        << " = \");\n";
+
+                    field->type().emitJavaDump(out, "builder", "this." + field->name() + "()");
+                    out << "break;\n";
+                }).endl();
+            }
+            else {
+                out << "builder.append(\"";
+                if (field != *(mFields->begin())) {
+                    out << ", ";
+                }
+                out << "." << field->name() << " = \");\n";
+                field->type().emitJavaDump(out, "builder", "this." + field->name());
+            }
+        }
+
+        if (mStyle == STYLE_SAFE_UNION && !mFields->empty()) {
+            out << "case hidl_discriminator.hidl_no_init: { break; }\n"
+                << "default: { throw new Error(\"Unknown union discriminator.\"); }\n";
+
+            out.unindent();
+            out << "}\n";
+        }
+
         out << "builder.append(\"}\");\nreturn builder.toString();\n";
     }).endl().endl();
 
-    size_t structAlign, structSize;
-    getAlignmentAndSize(&structAlign, &structSize);
+    CompoundLayout layout = getCompoundAlignmentAndSize();
 
     ////////////////////////////////////////////////////////////////////////////
 
     out << "public final void readFromParcel(android.os.HwParcel parcel) {\n";
     out.indent();
     if (containsInterface()) {
+
+        if (mStyle == STYLE_SAFE_UNION && !mFields->empty()) {
+            out << "hidl_d = ";
+            getUnionDiscriminatorType()->emitJavaReaderWriter(
+                    out, "parcel", "hidl_d", true);
+
+            out << "switch (hidl_d) {\n";
+            out.indent();
+        }
+
         for (const auto& field : *mFields) {
-            out << field->name() << " = ";
-            field->type().emitJavaReaderWriter(out, "parcel", field->name(), true);
+            if (mStyle == STYLE_SAFE_UNION) {
+                out << "case hidl_discriminator."
+                    << field->name()
+                    << ": ";
+
+                out.block([&] {
+                    out << "hidl_o = ";
+                    field->type().emitJavaReaderWriter(out, "parcel", "hidl_o", true);
+
+                    out << "break;\n";
+                }).endl();
+            } else {
+                out << field->name() << " = ";
+                field->type().emitJavaReaderWriter(out, "parcel", field->name(), true);
+            }
+        }
+
+        if (mStyle == STYLE_SAFE_UNION && !mFields->empty()) {
+            out << "case hidl_discriminator.hidl_no_init: { break; }\n"
+                << "default: { throw new Error(\"Unknown union discriminator.\"); }\n";
+
+            out.unindent();
+            out << "}\n";
         }
     } else {
         out << "android.os.HwBlob blob = parcel.readBuffer(";
-        out << structSize << "/* size */);\n";
+        out << layout.overall.size << " /* size */);\n";
         out << "readEmbeddedFromParcel(parcel, blob, 0 /* parentOffset */);\n";
     }
     out.unindent();
@@ -1327,17 +1529,51 @@
         out.indent(2);
         out << "android.os.HwParcel parcel, android.os.HwBlob _hidl_blob, long _hidl_offset) {\n";
         out.unindent();
-        size_t offset = 0;
+
+        if (mStyle == STYLE_SAFE_UNION && !mFields->empty()) {
+            getUnionDiscriminatorType()->emitJavaFieldReaderWriter(
+                out, 0 /* depth */, "parcel", "_hidl_blob", "hidl_d",
+                "_hidl_offset + " + std::to_string(layout.discriminator.offset),
+                true /* isReader */);
+
+            out << "switch (this.hidl_d) {\n";
+            out.indent();
+        }
+
+        size_t offset = layout.innerStruct.offset;
         for (const auto& field : *mFields) {
-            size_t fieldAlign, fieldSize;
-            field->type().getAlignmentAndSize(&fieldAlign, &fieldSize);
 
-            offset += Layout::getPad(offset, fieldAlign);
+            if (mStyle == STYLE_SAFE_UNION) {
+                out << "case hidl_discriminator."
+                    << field->name()
+                    << ": ";
 
-            field->type().emitJavaFieldReaderWriter(
-                out, 0 /* depth */, "parcel", "_hidl_blob", field->name(),
-                "_hidl_offset + " + std::to_string(offset), true /* isReader */);
-            offset += fieldSize;
+                out.block([&] {
+                    field->type().emitJavaFieldDefaultInitialValue(out, "hidl_o");
+                    field->type().emitJavaFieldReaderWriter(
+                        out, 0 /* depth */, "parcel", "_hidl_blob", "hidl_o",
+                        "_hidl_offset + " + std::to_string(offset), true /* isReader */);
+
+                    out << "break;\n";
+                }).endl();
+            } else {
+                size_t fieldAlign, fieldSize;
+                field->type().getAlignmentAndSize(&fieldAlign, &fieldSize);
+
+                offset += Layout::getPad(offset, fieldAlign);
+                field->type().emitJavaFieldReaderWriter(
+                    out, 0 /* depth */, "parcel", "_hidl_blob", field->name(),
+                    "_hidl_offset + " + std::to_string(offset), true /* isReader */);
+                offset += fieldSize;
+            }
+        }
+
+        if (mStyle == STYLE_SAFE_UNION && !mFields->empty()) {
+            out << "case hidl_discriminator.hidl_no_init: { break; }\n"
+                << "default: { throw new Error(\"Unknown union discriminator.\"); }\n";
+
+            out.unindent();
+            out << "}\n";
         }
         out.unindent();
         out << "}\n\n";
@@ -1349,11 +1585,39 @@
     out.indent();
 
     if (containsInterface()) {
+        if (mStyle == STYLE_SAFE_UNION && !mFields->empty()) {
+            getUnionDiscriminatorType()->emitJavaReaderWriter(
+                out, "parcel", "hidl_d", false);
+
+            out << "switch (this.hidl_d) {\n";
+            out.indent();
+        }
+
         for (const auto& field : *mFields) {
-            field->type().emitJavaReaderWriter(out, "parcel", field->name(), false);
+            if (mStyle == STYLE_SAFE_UNION) {
+                out << "case hidl_discriminator."
+                    << field->name()
+                    << ": ";
+
+                out.block([&] {
+                    field->type().emitJavaReaderWriter(out, "parcel", field->name() + "()", false);
+                    out << "break;\n";
+                }).endl();
+            } else {
+                field->type().emitJavaReaderWriter(out, "parcel", field->name(), false);
+            }
+        }
+
+        if (mStyle == STYLE_SAFE_UNION && !mFields->empty()) {
+            out << "case hidl_discriminator.hidl_no_init: { break; }\n"
+                << "default: { throw new Error(\"Unknown union discriminator.\"); }\n";
+
+            out.unindent();
+            out << "}\n";
         }
     } else {
-        out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob(" << structSize
+        out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob("
+            << layout.overall.size
             << " /* size */);\n";
 
         out << "writeEmbeddedToBlob(_hidl_blob, 0 /* parentOffset */);\n"
@@ -1371,10 +1635,10 @@
 
     if (containsInterface()) {
         out << "parcel.writeInt32(_hidl_vec.size());\n";
-        out << "for(" << fullJavaName() << " tmp: _hidl_vec)\n";
-        out.indent();
-        emitJavaReaderWriter(out, "parcel", "tmp", false);
-        out.unindent();
+        out << "for(" << fullJavaName() << " tmp: _hidl_vec) ";
+        out.block([&] {
+            emitJavaReaderWriter(out, "parcel", "tmp", false);
+        }).endl();
     } else {
         out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob(" << vecSize
             << " /* sizeof(hidl_vec<T>) */);\n";
@@ -1390,25 +1654,56 @@
     ////////////////////////////////////////////////////////////////////////////
 
     if (containsInterface()) {
-        out << "// writeEmbeddedFromParcel() is not generated\n";
+        out << "// writeEmbeddedToBlob() is not generated\n";
     } else {
         out << "public final void writeEmbeddedToBlob(\n";
         out.indent(2);
         out << "android.os.HwBlob _hidl_blob, long _hidl_offset) {\n";
         out.unindent();
-        size_t offset = 0;
-        for (const auto& field : *mFields) {
-            size_t fieldAlign, fieldSize;
-            field->type().getAlignmentAndSize(&fieldAlign, &fieldSize);
 
-            offset += Layout::getPad(offset, fieldAlign);
+        if (mStyle == STYLE_SAFE_UNION && !mFields->empty()) {
+            getUnionDiscriminatorType()->emitJavaFieldReaderWriter(
+                out, 0 /* depth */, "parcel", "_hidl_blob", "hidl_d",
+                "_hidl_offset + " + std::to_string(layout.discriminator.offset),
+                false /* isReader */);
 
-            field->type().emitJavaFieldReaderWriter(
-                out, 0 /* depth */, "parcel", "_hidl_blob", field->name(),
-                "_hidl_offset + " + std::to_string(offset), false /* isReader */);
-            offset += fieldSize;
+            out << "switch (this.hidl_d) {\n";
+            out.indent();
         }
 
+        size_t offset = layout.innerStruct.offset;
+        for (const auto& field : *mFields) {
+            if (mStyle == STYLE_SAFE_UNION) {
+                out << "case hidl_discriminator."
+                    << field->name()
+                    << ": ";
+
+                out.block([&] {
+                    field->type().emitJavaFieldReaderWriter(
+                        out, 0 /* depth */, "parcel", "_hidl_blob", field->name() + "()",
+                        "_hidl_offset + " + std::to_string(offset), false /* isReader */);
+
+                    out << "break;\n";
+                }).endl();
+            } else {
+                size_t fieldAlign, fieldSize;
+                field->type().getAlignmentAndSize(&fieldAlign, &fieldSize);
+
+                offset += Layout::getPad(offset, fieldAlign);
+                field->type().emitJavaFieldReaderWriter(
+                    out, 0 /* depth */, "parcel", "_hidl_blob", field->name(),
+                    "_hidl_offset + " + std::to_string(offset), false /* isReader */);
+                offset += fieldSize;
+            }
+        }
+
+        if (mStyle == STYLE_SAFE_UNION && !mFields->empty()) {
+            out << "case hidl_discriminator.hidl_no_init: { break; }\n"
+                << "default: { throw new Error(\"Unknown union discriminator.\"); }\n";
+
+            out.unindent();
+            out << "}\n";
+        }
         out.unindent();
         out << "}\n";
     }
@@ -1723,7 +2018,7 @@
 }
 
 bool CompoundType::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
-    if (mStyle != STYLE_STRUCT) {  // TODO(natre): Update
+    if (mStyle == STYLE_UNION) {
         return false;
     }
 
diff --git a/CompoundType.h b/CompoundType.h
index 305162b..e2b6612 100644
--- a/CompoundType.h
+++ b/CompoundType.h
@@ -110,6 +110,9 @@
     void emitJavaFieldInitializer(
             Formatter &out, const std::string &fieldName) const override;
 
+    void emitJavaFieldDefaultInitialValue(
+            Formatter &out, const std::string &declaredFieldName) const override;
+
     void emitJavaFieldReaderWriter(
             Formatter &out,
             size_t depth,
diff --git a/Coordinator.cpp b/Coordinator.cpp
index 9a2009c..0e77ef3 100644
--- a/Coordinator.cpp
+++ b/Coordinator.cpp
@@ -320,7 +320,7 @@
                     fqName.name().c_str());
 
             err = UNKNOWN_ERROR;
-        } else if ((*ast)->containsInterfaces()) {
+        } else if ((*ast)->definesInterfaces()) {
             fprintf(stderr,
                     "ERROR: types.hal file at '%s' declares at least one "
                     "interface type.\n",
diff --git a/EnumType.cpp b/EnumType.cpp
index d7fca9c..2c9c0cc 100644
--- a/EnumType.cpp
+++ b/EnumType.cpp
@@ -168,8 +168,8 @@
     return mStorageType->resolveToScalarType()->getJavaSuffix();
 }
 
-std::string EnumType::getJavaWrapperType() const {
-    return mStorageType->resolveToScalarType()->getJavaWrapperType();
+std::string EnumType::getJavaTypeClass() const {
+    return mStorageType->resolveToScalarType()->getJavaTypeClass();
 }
 
 std::string EnumType::getVtsType() const {
@@ -185,8 +185,8 @@
     return resolveToScalarType()->getJavaType(forInitializer);
 }
 
-std::string EnumType::getBitfieldJavaWrapperType() const {
-    return resolveToScalarType()->getJavaWrapperType();
+std::string EnumType::getBitfieldJavaTypeClass() const {
+    return resolveToScalarType()->getJavaTypeClass();
 }
 
 LocalIdentifier *EnumType::lookupIdentifier(const std::string &name) const {
@@ -590,7 +590,6 @@
     }).endl();
 
     auto bitfieldType = getBitfieldJavaType(false /* forInitializer */);
-    auto bitfieldWrapperType = getBitfieldJavaWrapperType();
     out << "\n"
         << "public static final String dumpBitfield("
         << bitfieldType << " o) ";
@@ -944,8 +943,8 @@
     return resolveToScalarType()->getJavaSuffix();
 }
 
-std::string BitFieldType::getJavaWrapperType() const {
-    return getElementEnumType()->getBitfieldJavaWrapperType();
+std::string BitFieldType::getJavaTypeClass() const {
+    return getElementEnumType()->getBitfieldJavaTypeClass();
 }
 
 std::string BitFieldType::getVtsType() const {
diff --git a/EnumType.h b/EnumType.h
index 37fa00e..309eeb0 100644
--- a/EnumType.h
+++ b/EnumType.h
@@ -52,16 +52,15 @@
                            bool specifyNamespaces) const override;
 
     std::string getJavaType(bool forInitializer) const override;
+    std::string getJavaTypeClass() const override;
 
     std::string getJavaSuffix() const override;
 
-    std::string getJavaWrapperType() const override;
-
     std::string getVtsType() const override;
 
     std::string getBitfieldCppType(StorageMode mode, bool specifyNamespaces = true) const;
     std::string getBitfieldJavaType(bool forInitializer = false) const;
-    std::string getBitfieldJavaWrapperType() const;
+    std::string getBitfieldJavaTypeClass() const;
 
     // Return the type that corresponds to bitfield<T>.
     const BitFieldType* getBitfieldType() const;
@@ -184,11 +183,10 @@
                            bool specifyNamespaces) const override;
 
     std::string getJavaType(bool forInitializer) const override;
+    std::string getJavaTypeClass() const override;
 
     std::string getJavaSuffix() const override;
 
-    std::string getJavaWrapperType() const override;
-
     std::string getVtsType() const override;
 
     const EnumType* getEnumType() const;
diff --git a/ScalarType.cpp b/ScalarType.cpp
index 2e2e7b2..dc02b69 100644
--- a/ScalarType.cpp
+++ b/ScalarType.cpp
@@ -83,7 +83,7 @@
     return kName[mKind];
 }
 
-std::string ScalarType::getJavaWrapperType() const {
+std::string ScalarType::getJavaTypeClass() const {
     static const char *const kName[] = {
         "Boolean",
         "Byte",
@@ -225,7 +225,7 @@
         case KIND_INT16:    // fallthrough
         case KIND_UINT16: {
             // Because Byte and Short doesn't have toHexString, we have to use Integer.toHexString.
-            out << "Integer.toHexString(" << getJavaWrapperType() << ".toUnsignedInt(("
+            out << "Integer.toHexString(" << getJavaTypeClass() << ".toUnsignedInt(("
                 << getJavaType(false /* forInitializer */) << ")(" << name << ")))";
             break;
         }
@@ -233,7 +233,7 @@
         case KIND_UINT32:   // fallthrough
         case KIND_INT64:    // fallthrough
         case KIND_UINT64: {
-            out << getJavaWrapperType() << ".toHexString(" << name << ")";
+            out << getJavaTypeClass() << ".toHexString(" << name << ")";
             break;
         }
         case KIND_FLOAT:    // fallthrough
diff --git a/ScalarType.h b/ScalarType.h
index 86305b8..10fb540 100644
--- a/ScalarType.h
+++ b/ScalarType.h
@@ -54,8 +54,8 @@
             bool specifyNamespaces) const override;
 
     std::string getJavaType(bool forInitializer) const override;
+    std::string getJavaTypeClass() const override;
 
-    std::string getJavaWrapperType() const override;
     std::string getJavaSuffix() const override;
 
     std::string getVtsType() const override;
diff --git a/Scope.cpp b/Scope.cpp
index bff097f..fcf67bf 100644
--- a/Scope.cpp
+++ b/Scope.cpp
@@ -96,7 +96,7 @@
     return NULL;
 }
 
-bool Scope::containsInterfaces() const {
+bool Scope::definesInterfaces() const {
     for (const NamedType *type : mTypes) {
         if (type->isInterface()) {
             return true;
diff --git a/Scope.h b/Scope.h
index e8c9431..cfef50e 100644
--- a/Scope.h
+++ b/Scope.h
@@ -51,7 +51,7 @@
     // Returns the single interface or NULL.
     Interface *getInterface() const;
 
-    bool containsInterfaces() const;
+    bool definesInterfaces() const;
 
     const std::vector<Annotation*>& annotations() const;
 
diff --git a/StringType.cpp b/StringType.cpp
index a07086f..b4f72be 100644
--- a/StringType.cpp
+++ b/StringType.cpp
@@ -150,9 +150,12 @@
 
 void StringType::emitJavaFieldInitializer(
         Formatter &out, const std::string &fieldName) const {
-    out << "String "
-        << fieldName
-        << " = new String();\n";
+    emitJavaFieldDefaultInitialValue(out, "String " + fieldName);
+}
+
+void StringType::emitJavaFieldDefaultInitialValue(
+        Formatter &out, const std::string &declaredFieldName) const {
+    out << declaredFieldName << " = new String();\n";
 }
 
 void StringType::emitJavaFieldReaderWriter(
@@ -180,7 +183,7 @@
 
         // hidl_string's embedded buffer is never null(able), because it defaults to a
         // buffer containing an empty string.
-        out << fieldName << ".getBytes().length + 1,\n"
+        out << "(" << getJavaTypeCast(fieldName) << ").getBytes().length + 1,\n"
             << blobName
             << ".handle(),\n"
             << offset
diff --git a/StringType.h b/StringType.h
index a4e3691..2050875 100644
--- a/StringType.h
+++ b/StringType.h
@@ -65,6 +65,9 @@
     void emitJavaFieldInitializer(
             Formatter &out, const std::string &fieldName) const override;
 
+    void emitJavaFieldDefaultInitialValue(
+            Formatter &out, const std::string &declaredFieldName) const override;
+
     void emitJavaFieldReaderWriter(
             Formatter &out,
             size_t depth,
diff --git a/Type.cpp b/Type.cpp
index 00bbae3..33a8aa1 100644
--- a/Type.cpp
+++ b/Type.cpp
@@ -385,10 +385,14 @@
     return std::string();
 }
 
-std::string Type::getJavaWrapperType() const {
+std::string Type::getJavaTypeClass() const {
     return getJavaType();
 }
 
+std::string Type::getJavaTypeCast(const std::string& objName) const {
+    return "(" + getJavaType() + ") " + objName;
+}
+
 std::string Type::getJavaSuffix() const {
     CHECK(!"Should not be here");
     return std::string();
@@ -513,6 +517,8 @@
         << ";\n";
 }
 
+void Type::emitJavaFieldDefaultInitialValue(Formatter &, const std::string &) const {}
+
 void Type::emitJavaFieldReaderWriter(
         Formatter &,
         size_t,
diff --git a/Type.h b/Type.h
index 1653cdd..6426de4 100644
--- a/Type.h
+++ b/Type.h
@@ -170,7 +170,13 @@
     // otherwise (and by default), they are omitted, i.e. [][].
     virtual std::string getJavaType(bool forInitializer = false) const;
 
-    virtual std::string getJavaWrapperType() const;
+    // Identical to getJavaType() for most types, except: primitives, in which
+    // case the wrapper type is returned, and generics (such as ArrayList<?>),
+    // where the type specialization is omitted to facilitate use of
+    // instanceof or class.isInstance().
+    virtual std::string getJavaTypeClass() const;
+
+    virtual std::string getJavaTypeCast(const std::string& objName) const;
     virtual std::string getJavaSuffix() const;
 
     virtual std::string getVtsType() const;
@@ -249,6 +255,10 @@
             Formatter &out,
             const std::string &fieldName) const;
 
+    virtual void emitJavaFieldDefaultInitialValue(
+            Formatter &out,
+            const std::string &declaredFieldName) const;
+
     virtual void emitJavaFieldReaderWriter(
             Formatter &out,
             size_t depth,
diff --git a/VectorType.cpp b/VectorType.cpp
index 03604dc..3ba3df5 100644
--- a/VectorType.cpp
+++ b/VectorType.cpp
@@ -45,6 +45,9 @@
         return true;
     }
     if (elementType->isCompoundType()) {
+        if (static_cast<const CompoundType*>(elementType)->containsInterface()) {
+            return false;
+        }
         return true;
     }
     if (elementType->isInterface()) {
@@ -110,17 +113,15 @@
 }
 
 std::string VectorType::getJavaType(bool /* forInitializer */) const {
+    const std::string elementJavaType = mElementType->isTemplatedType()
+        ? mElementType->getJavaType()
+        : mElementType->getJavaTypeClass();
 
-    std::string elementJavaType;
-    if (mElementType->isArray()) {
-        elementJavaType = mElementType->getJavaType();
-    } else {
-        elementJavaType = mElementType->getJavaWrapperType();
-    }
+    return "java.util.ArrayList<" + elementJavaType + ">";
+}
 
-    return "java.util.ArrayList<"
-        + elementJavaType
-        + ">";
+std::string VectorType::getJavaTypeClass() const {
+    return "java.util.ArrayList";
 }
 
 std::string VectorType::getVtsType() const {
@@ -544,14 +545,17 @@
 
 void VectorType::emitJavaFieldInitializer(
         Formatter &out, const std::string &fieldName) const {
-    std::string javaType = getJavaType(false /* forInitializer */);
+    const std::string typeName = getJavaType(false /* forInitializer */);
+    const std::string fieldDeclaration = "final " + typeName + " " + fieldName;
 
-    out << "final "
-        << javaType
-        << " "
-        << fieldName
+    emitJavaFieldDefaultInitialValue(out, fieldDeclaration);
+}
+
+void VectorType::emitJavaFieldDefaultInitialValue(
+        Formatter &out, const std::string &declaredFieldName) const {
+    out << declaredFieldName
         << " = new "
-        << javaType
+        << getJavaType(false /* forInitializer */)
         << "();\n";
 }
 
@@ -563,13 +567,18 @@
         const std::string &fieldName,
         const std::string &offset,
         bool isReader) const {
+
+    const std::string fieldNameWithCast = isReader
+        ? "(" + getJavaTypeCast(fieldName) + ")"
+        : fieldName;
+
     VectorType::EmitJavaFieldReaderWriterForElementType(
             out,
             depth,
             mElementType.get(),
             parcelName,
             blobName,
-            fieldName,
+            fieldNameWithCast,
             offset,
             isReader);
 }
diff --git a/VectorType.h b/VectorType.h
index 6f6ede7..cc889c9 100644
--- a/VectorType.h
+++ b/VectorType.h
@@ -43,6 +43,7 @@
             bool specifyNamespaces) const override;
 
     std::string getJavaType(bool forInitializer) const override;
+    std::string getJavaTypeClass() const override;
 
     std::string getVtsType() const override;
     std::string getVtsValueName() const override;
@@ -101,6 +102,9 @@
     void emitJavaFieldInitializer(
             Formatter &out, const std::string &fieldName) const override;
 
+    void emitJavaFieldDefaultInitialValue(
+            Formatter &out, const std::string &declaredFieldName) const override;
+
     void emitJavaFieldReaderWriter(
             Formatter &out,
             size_t depth,
diff --git a/build/hidl_interface.go b/build/hidl_interface.go
index 375c656..9f2be74 100644
--- a/build/hidl_interface.go
+++ b/build/hidl_interface.go
@@ -175,7 +175,7 @@
 }
 
 func hidlGenCommand(lang string, roots []string, name *fqName) *string {
-	cmd := "$(location hidl-gen) -d $(depfile) -o $(genDir)"
+	cmd := "$(location hidl-gen) -p . -d $(depfile) -o $(genDir)"
 	cmd += " -L" + lang
 	cmd += " " + strings.Join(wrap("-r", roots, ""), " ")
 	cmd += " " + name.string()
@@ -288,11 +288,12 @@
 			Out: concat(wrap(name.sanitizedDir()+"I", interfaces, ".java"),
 				wrap(name.sanitizedDir(), i.properties.Types, ".java")),
 		})
-		mctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory(true)), &javaProperties{
+		mctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory), &javaProperties{
 			Name:              proptools.StringPtr(name.javaName()),
 			Owner:             i.properties.Owner,
 			Defaults:          []string{"hidl-java-module-defaults"},
 			No_framework_libs: proptools.BoolPtr(true),
+			Installable:       proptools.BoolPtr(true),
 			Srcs:              []string{":" + name.javaSourcesName()},
 			Static_libs:       javaDependencies,
 
@@ -315,7 +316,7 @@
 			Srcs:    i.properties.Srcs,
 			Out:     []string{name.sanitizedDir() + "Constants.java"},
 		})
-		mctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory(true)), &javaProperties{
+		mctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory), &javaProperties{
 			Name:              proptools.StringPtr(name.javaConstantsName()),
 			Owner:             i.properties.Owner,
 			Defaults:          []string{"hidl-java-module-defaults"},
@@ -435,7 +436,7 @@
 	return nil
 }
 
-var doubleLoadablePackageNames = []string {
+var doubleLoadablePackageNames = []string{
 	"android.hardware.configstore@",
 	"android.hardware.graphics.allocator@",
 	"android.hardware.graphics.bufferqueue@",
diff --git a/build/properties.go b/build/properties.go
index 0798157..e0abcf4 100644
--- a/build/properties.go
+++ b/build/properties.go
@@ -56,6 +56,7 @@
 	Owner             *string
 	Defaults          []string
 	No_framework_libs *bool
+	Installable       *bool
 	Sdk_version       *string
 	Srcs              []string
 	Libs              []string