[automerger skipped] Merge "Zero hidl-generated structs" into oc-dr1-dev am: c62f78a630 -s ours
am: 205dd6ed92 -s ours
am skip reason: change_id I8fb10532b6fdf9afa419f19fc634981e7cbd3c10 with SHA1 da1d449882 is in history

Change-Id: I7ad06c8e0212e5fbb5b56ea2c707552a1a7b4771
diff --git a/AST.cpp b/AST.cpp
index e33fbbf..cb260e2 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -18,10 +18,10 @@
 
 #include "Coordinator.h"
 #include "EnumType.h"
+#include "FmqType.h"
 #include "HandleType.h"
 #include "Interface.h"
 #include "Location.h"
-#include "FmqType.h"
 #include "Scope.h"
 #include "TypeDef.h"
 
@@ -35,10 +35,11 @@
 
 namespace android {
 
-AST::AST(const Coordinator* coordinator, const std::string& path)
+AST::AST(const Coordinator* coordinator, const Hash* fileHash)
     : mCoordinator(coordinator),
-      mPath(path),
-      mRootScope("(root scope)", Location::startOf(path), nullptr /* parent */) {}
+      mFileHash(fileHash),
+      mRootScope("(root scope)", FQName(), Location::startOf(fileHash->getPath()),
+                 nullptr /* parent */) {}
 
 Scope* AST::getRootScope() {
     return &mRootScope;
@@ -53,13 +54,17 @@
     return mSyntaxErrors;
 }
 
-const std::string &AST::getFilename() const {
-    return mPath;
+const std::string& AST::getFilename() const {
+    return mFileHash->getPath();
+}
+const Hash* AST::getFileHash() const {
+    return mFileHash;
 }
 
 bool AST::setPackage(const char *package) {
-    mPackage.setTo(package);
-    CHECK(mPackage.isValid());
+    if (!mPackage.setTo(package)) {
+        return false;
+    }
 
     if (mPackage.package().empty()
             || mPackage.version().empty()
@@ -82,16 +87,235 @@
     return mRootScope.containsInterfaces();
 }
 
+status_t AST::postParse() {
+    status_t err;
+
+    // lookupTypes is the first pass.
+    err = lookupTypes();
+    if (err != OK) return err;
+    // validateDefinedTypesUniqueNames is the first call
+    // after lookup, as other errors could appear because
+    // user meant different type than we assumed.
+    err = validateDefinedTypesUniqueNames();
+    if (err != OK) return err;
+    // topologicalReorder is before resolveInheritance, as we
+    // need to have no cycle while getting parent class.
+    err = topologicalReorder();
+    if (err != OK) return err;
+    err = resolveInheritance();
+    if (err != OK) return err;
+    err = lookupLocalIdentifiers();
+    if (err != OK) return err;
+    // checkAcyclicConstantExpressions is after resolveInheritance,
+    // as resolveInheritance autofills enum values.
+    err = checkAcyclicConstantExpressions();
+    if (err != OK) return err;
+    err = evaluate();
+    if (err != OK) return err;
+    err = validate();
+    if (err != OK) return err;
+    err = checkForwardReferenceRestrictions();
+    if (err != OK) return err;
+    err = gatherReferencedTypes();
+    if (err != OK) return err;
+
+    // Make future packages not to call passes
+    // for processed types and expressions
+    constantExpressionRecursivePass(
+        [](ConstantExpression* ce) {
+            ce->setPostParseCompleted();
+            return OK;
+        },
+        true /* processBeforeDependencies */);
+    std::unordered_set<const Type*> visited;
+    mRootScope.recursivePass(
+        [](Type* type) {
+            type->setPostParseCompleted();
+            return OK;
+        },
+        &visited);
+
+    return OK;
+}
+
+status_t AST::constantExpressionRecursivePass(
+    const std::function<status_t(ConstantExpression*)>& func, bool processBeforeDependencies) {
+    std::unordered_set<const Type*> visitedTypes;
+    std::unordered_set<const ConstantExpression*> visitedCE;
+    return mRootScope.recursivePass(
+        [&](Type* type) -> status_t {
+            for (auto* ce : type->getConstantExpressions()) {
+                status_t err = ce->recursivePass(func, &visitedCE, processBeforeDependencies);
+                if (err != OK) return err;
+            }
+            return OK;
+        },
+        &visitedTypes);
+}
+
+status_t AST::lookupTypes() {
+    std::unordered_set<const Type*> visited;
+    return mRootScope.recursivePass(
+        [&](Type* type) -> status_t {
+            Scope* scope = type->isScope() ? static_cast<Scope*>(type) : type->parent();
+
+            for (auto* nextRef : type->getReferences()) {
+                if (nextRef->isResolved()) {
+                    continue;
+                }
+
+                Type* nextType = lookupType(nextRef->getLookupFqName(), scope);
+                if (nextType == nullptr) {
+                    std::cerr << "ERROR: Failed to lookup type '"
+                              << nextRef->getLookupFqName().string() << "' at "
+                              << nextRef->location() << "\n";
+                    return UNKNOWN_ERROR;
+                }
+                nextRef->set(nextType);
+            }
+
+            return OK;
+        },
+        &visited);
+}
+
+status_t AST::gatherReferencedTypes() {
+    std::unordered_set<const Type*> visited;
+    return mRootScope.recursivePass(
+        [&](Type* type) -> status_t {
+            for (auto* nextRef : type->getReferences()) {
+                const Type *targetType = nextRef->get();
+                if (targetType->isNamedType()) {
+                    mReferencedTypeNames.insert(
+                            static_cast<const NamedType *>(targetType)->fqName());
+                }
+            }
+
+            return OK;
+        },
+        &visited);
+}
+
+status_t AST::lookupLocalIdentifiers() {
+    std::unordered_set<const Type*> visitedTypes;
+    std::unordered_set<const ConstantExpression*> visitedCE;
+
+    return mRootScope.recursivePass(
+        [&](Type* type) -> status_t {
+            Scope* scope = type->isScope() ? static_cast<Scope*>(type) : type->parent();
+
+            for (auto* ce : type->getConstantExpressions()) {
+                status_t err = ce->recursivePass(
+                    [&](ConstantExpression* ce) {
+                        for (auto* nextRef : ce->getReferences()) {
+                            if (nextRef->isResolved()) continue;
+
+                            LocalIdentifier* iden = lookupLocalIdentifier(*nextRef, scope);
+                            if (iden == nullptr) return UNKNOWN_ERROR;
+                            nextRef->set(iden);
+                        }
+                        return OK;
+                    },
+                    &visitedCE, true /* processBeforeDependencies */);
+                if (err != OK) return err;
+            }
+
+            return OK;
+        },
+        &visitedTypes);
+}
+
+status_t AST::validateDefinedTypesUniqueNames() const {
+    std::unordered_set<const Type*> visited;
+    return mRootScope.recursivePass(
+        [&](const Type* type) -> status_t {
+            // We only want to validate type definition names in this place.
+            if (type->isScope()) {
+                return static_cast<const Scope*>(type)->validateUniqueNames();
+            }
+            return OK;
+        },
+        &visited);
+}
+
+status_t AST::resolveInheritance() {
+    std::unordered_set<const Type*> visited;
+    return mRootScope.recursivePass(&Type::resolveInheritance, &visited);
+}
+
+status_t AST::evaluate() {
+    return constantExpressionRecursivePass(
+        [](ConstantExpression* ce) {
+            ce->evaluate();
+            return OK;
+        },
+        false /* processBeforeDependencies */);
+}
+
+status_t AST::validate() const {
+    std::unordered_set<const Type*> visited;
+    return mRootScope.recursivePass(&Type::validate, &visited);
+}
+
+status_t AST::topologicalReorder() {
+    std::unordered_map<const Type*, size_t> reversedOrder;
+    std::unordered_set<const Type*> stack;
+    status_t err = mRootScope.topologicalOrder(&reversedOrder, &stack).status;
+    if (err != OK) return err;
+
+    std::unordered_set<const Type*> visited;
+    mRootScope.recursivePass(
+        [&](Type* type) {
+            if (type->isScope()) {
+                static_cast<Scope*>(type)->topologicalReorder(reversedOrder);
+            }
+            return OK;
+        },
+        &visited);
+    return OK;
+}
+
+status_t AST::checkAcyclicConstantExpressions() const {
+    std::unordered_set<const Type*> visitedTypes;
+    std::unordered_set<const ConstantExpression*> visitedCE;
+    std::unordered_set<const ConstantExpression*> stack;
+    return mRootScope.recursivePass(
+        [&](const Type* type) -> status_t {
+            for (auto* ce : type->getConstantExpressions()) {
+                status_t err = ce->checkAcyclic(&visitedCE, &stack).status;
+                CHECK(err != OK || stack.empty());
+                if (err != OK) return err;
+            }
+            return OK;
+        },
+        &visitedTypes);
+}
+
+status_t AST::checkForwardReferenceRestrictions() const {
+    std::unordered_set<const Type*> visited;
+    return mRootScope.recursivePass(
+        [](const Type* type) -> status_t {
+            for (const Reference<Type>* ref : type->getReferences()) {
+                status_t err = type->checkForwardReferenceRestrictions(*ref);
+                if (err != OK) return err;
+            }
+            return OK;
+        },
+        &visited);
+}
+
 bool AST::addImport(const char *import) {
-    FQName fqName(import);
-    CHECK(fqName.isValid());
+    FQName fqName;
+    if (!FQName::parse(import, &fqName)) {
+        std::cerr << "ERROR: '" << import << "' is an invalid fully-qualified name." << std::endl;
+        return false;
+    }
 
     fqName.applyDefaults(mPackage.package(), mPackage.version());
 
-    // LOG(INFO) << "importing " << fqName.string();
-
     if (fqName.name().empty()) {
         // import a package
+
         std::vector<FQName> packageInterfaces;
 
         status_t err =
@@ -103,6 +327,8 @@
         }
 
         for (const auto &subFQName : packageInterfaces) {
+            addToImportedNamesGranular(subFQName);
+
             // Do not enforce restrictions on imports.
             AST* ast = mCoordinator->parse(subFQName, &mImportedASTs, Coordinator::Enforce::NONE);
             if (ast == nullptr) {
@@ -115,7 +341,7 @@
         return true;
     }
 
-    AST *importAST;
+    addToImportedNamesGranular(fqName);
 
     // cases like android.hardware.foo@1.0::IFoo.Internal
     //            android.hardware.foo@1.0::Abc.Internal
@@ -123,7 +349,11 @@
     // assume it is an interface, and try to import it.
     const FQName interfaceName = fqName.getTopLevelType();
     // Do not enforce restrictions on imports.
-    importAST = mCoordinator->parse(interfaceName, &mImportedASTs, Coordinator::Enforce::NONE);
+    AST* importAST;
+    status_t err = mCoordinator->parseOptional(interfaceName, &importAST, &mImportedASTs,
+                                               Coordinator::Enforce::NONE);
+    if (err != OK) return false;
+    // importAST nullptr == file doesn't exist
 
     if (importAST != nullptr) {
         // cases like android.hardware.foo@1.0::IFoo.Internal
@@ -175,25 +405,8 @@
     mImportedASTs.insert(ast);
 }
 
-bool AST::addTypeDef(const char* localName, Type* type, const Location& location,
-                     std::string* errorMsg, Scope* scope) {
-    // The reason we wrap the given type in a TypeDef is simply to suppress
-    // emitting any type definitions later on, since this is just an alias
-    // to a type defined elsewhere.
-    return addScopedTypeInternal(new TypeDef(localName, location, scope, type), errorMsg, scope);
-}
-
-bool AST::addScopedType(NamedType* type, std::string* errorMsg, Scope* scope) {
-    return addScopedTypeInternal(type, errorMsg, scope);
-}
-
-bool AST::addScopedTypeInternal(NamedType* type, std::string* errorMsg, Scope* scope) {
-    bool success = scope->addType(type, errorMsg);
-    if (!success) {
-        return false;
-    }
-
-    std::vector<std::string> pathComponents{{type->localName()}};
+FQName AST::makeFullName(const char* localName, Scope* scope) const {
+    std::vector<std::string> pathComponents{{localName}};
     for (; scope != &mRootScope; scope = scope->parent()) {
         pathComponents.push_back(scope->localName());
     }
@@ -201,12 +414,34 @@
     std::reverse(pathComponents.begin(), pathComponents.end());
     std::string path = StringHelper::JoinStrings(pathComponents, ".");
 
-    FQName fqName(mPackage.package(), mPackage.version(), path);
-    type->setFullName(fqName);
+    return FQName(mPackage.package(), mPackage.version(), path);
+}
 
-    mDefinedTypesByFullName[fqName] = type;
+void AST::addScopedType(NamedType* type, Scope* scope) {
+    scope->addType(type);
+    mDefinedTypesByFullName[type->fqName()] = type;
+}
 
-    return true;
+LocalIdentifier* AST::lookupLocalIdentifier(const Reference<LocalIdentifier>& ref, Scope* scope) {
+    const FQName& fqName = ref.getLookupFqName();
+
+    if (fqName.isIdentifier()) {
+        LocalIdentifier* iden = scope->lookupIdentifier(fqName.name());
+        if (iden == nullptr) {
+            std::cerr << "ERROR: identifier " << fqName.string() << " could not be found at "
+                      << ref.location() << "\n";
+            return nullptr;
+        }
+        return iden;
+    } else {
+        std::string errorMsg;
+        EnumValue* enumValue = lookupEnumValue(fqName, &errorMsg, scope);
+        if (enumValue == nullptr) {
+            std::cerr << "ERROR: " << errorMsg << " at " << ref.location() << "\n";
+            return nullptr;
+        }
+        return enumValue;
+    }
 }
 
 EnumValue* AST::lookupEnumValue(const FQName& fqName, std::string* errorMsg, Scope* scope) {
@@ -221,6 +456,7 @@
         *errorMsg = "Cannot find type " + enumTypeName.string();
         return nullptr;
     }
+    type = type->resolve();
     if(!type->isEnum()) {
         *errorMsg = "Type " + enumTypeName.string() + " is not an enum type";
         return nullptr;
@@ -232,6 +468,9 @@
         *errorMsg = "Enum type " + enumTypeName.string() + " does not have " + enumValueName;
         return nullptr;
     }
+
+    mReferencedTypeNames.insert(enumType->fqName());
+
     return v;
 }
 
@@ -253,14 +492,12 @@
         }
     }
 
-    if (!fqName.isFullyQualified()) {
-        status_t status = lookupAutofilledType(fqName, &returnedType);
-        if (status != OK) {
-            return nullptr;
-        }
-        if (returnedType != nullptr) {
-            return returnedType;
-        }
+    status_t status = lookupAutofilledType(fqName, &returnedType);
+    if (status != OK) {
+        return nullptr;
+    }
+    if (returnedType != nullptr) {
+        return returnedType;
     }
 
     return lookupTypeFromImports(fqName);
@@ -273,13 +510,7 @@
 
     for (; scope != nullptr; scope = scope->parent()) {
         Type* type = scope->lookupType(fqName);
-
         if (type != nullptr) {
-            // Resolve typeDefs to the target type.
-            while (type->isTypeDef()) {
-                type = static_cast<TypeDef *>(type)->referencedType();
-            }
-
             return type;
         }
     }
@@ -289,7 +520,7 @@
 
 // Rule 1: auto-fill with current package
 status_t AST::lookupAutofilledType(const FQName &fqName, Type **returnedType) {
-    CHECK(!fqName.isFullyQualified() && !fqName.name().empty() && fqName.valueName().empty());
+    CHECK(!fqName.name().empty() && fqName.valueName().empty());
 
     FQName autofilled = fqName;
     autofilled.applyDefaults(mPackage.package(), mPackage.version());
@@ -298,7 +529,7 @@
     // in import.
     Type *local = findDefinedType(autofilled, &matchingName);
     CHECK(local == nullptr || autofilled == matchingName);
-    Type* fromImport = lookupType(autofilled, nullptr /* scope */);
+    Type* fromImport = lookupTypeFromImports(autofilled);
 
     if (local != nullptr && fromImport != nullptr && local != fromImport) {
         // Something bad happen; two types have the same FQName.
@@ -382,20 +613,6 @@
     }
 
     if (resolvedType) {
-#if 0
-        LOG(INFO) << "found '"
-                  << resolvedName.string()
-                  << "' after looking for '"
-                  << fqName.string()
-                  << "'.";
-#endif
-
-        // Resolve typeDefs to the target type.
-        while (resolvedType->isTypeDef()) {
-            resolvedType =
-                static_cast<TypeDef *>(resolvedType)->referencedType();
-        }
-
         returnedType = resolvedType;
 
         // If the resolved type is not an interface, we need to determine
@@ -444,14 +661,26 @@
             // in turn referenced the found interface we'd mistakenly use the
             // name of the typedef instead of the proper name of the interface.
 
-            mImportedNames.insert(
-                    static_cast<Interface *>(resolvedType)->fqName());
+            const FQName &typeName =
+                static_cast<Interface *>(resolvedType)->fqName();
+
+            mImportedNames.insert(typeName);
         }
     }
 
     return returnedType;
 }
 
+void AST::addToImportedNamesGranular(const FQName &fqName) {
+    if (fqName.package() == package().package()
+            && fqName.version() == package().version()) {
+        // Our own names are _defined_ here, not imported.
+        return;
+    }
+
+    mImportedNamesGranular.insert(fqName);
+}
+
 Type *AST::findDefinedType(const FQName &fqName, FQName *matchingName) const {
     for (const auto &pair : mDefinedTypesByFullName) {
         const FQName &key = pair.first;
@@ -467,7 +696,7 @@
 }
 
 void AST::getImportedPackages(std::set<FQName> *importSet) const {
-    for (const auto &fqName : mImportedNames) {
+    for (const auto& fqName : mImportedNamesGranular) {
         FQName packageName = fqName.getPackageAndVersion();
 
         if (packageName == mPackage) {
@@ -481,6 +710,7 @@
 
 void AST::getImportedPackagesHierarchy(std::set<FQName> *importSet) const {
     getImportedPackages(importSet);
+
     std::set<FQName> newSet;
     for (const auto &ast : mImportedASTs) {
         if (importSet->find(ast->package()) != importSet->end()) {
@@ -498,19 +728,23 @@
     }
 }
 
-bool AST::isJavaCompatible() const {
-    if (!AST::isInterface()) {
-        for (const auto* type : mRootScope.getSubTypes()) {
-            if (!type->isJavaCompatible()) {
-                return false;
-            }
+void AST::getAllImportedNamesGranular(std::set<FQName> *allImportNames) const {
+    for (const auto& fqName : mImportedNamesGranular) {
+        if (fqName.name() == "types") {
+            // A package will export everything _defined_ but will not
+            // re-export anything it itself imported.
+            AST* ast = mCoordinator->parse(
+                    fqName, nullptr /* imported */, Coordinator::Enforce::NONE);
+
+            ast->addDefinedTypes(allImportNames);
+        } else {
+            allImportNames->insert(fqName);
         }
-
-        return true;
     }
+}
 
-    const Interface* iface = mRootScope.getInterface();
-    return iface->isJavaCompatible();
+bool AST::isJavaCompatible() const {
+    return mRootScope.isJavaCompatible();
 }
 
 void AST::appendToExportedTypesVector(
@@ -533,4 +767,24 @@
     return iface ? iface->getBaseName() : "types";
 }
 
+void AST::addDefinedTypes(std::set<FQName> *definedTypes) const {
+    std::for_each(
+            mDefinedTypesByFullName.begin(),
+            mDefinedTypesByFullName.end(),
+            [definedTypes](const auto &elem) {
+                if (!elem.second->isTypeDef()) {
+                    definedTypes->insert(elem.first);
+                }
+            });
+}
+
+void AST::addReferencedTypes(std::set<FQName> *referencedTypes) const {
+    std::for_each(
+            mReferencedTypeNames.begin(),
+            mReferencedTypeNames.end(),
+            [referencedTypes](const auto &fqName) {
+                referencedTypes->insert(fqName);
+            });
+}
+
 }  // namespace android;
diff --git a/AST.h b/AST.h
index 6ef0ed3..4de98bd 100644
--- a/AST.h
+++ b/AST.h
@@ -19,7 +19,9 @@
 #define AST_H_
 
 #include <android-base/macros.h>
+#include <hidl-hash/Hash.h>
 #include <hidl-util/FQName.h>
+#include <functional>
 #include <map>
 #include <set>
 #include <string>
@@ -31,16 +33,19 @@
 namespace android {
 
 struct Coordinator;
+struct ConstantExpression;
+struct EnumValue;
 struct Formatter;
 struct Interface;
 struct Location;
 struct Method;
 struct NamedType;
-struct TypedVar;
-struct EnumValue;
+template <class T>
+struct NamedReference;
+struct Type;
 
 struct AST {
-    AST(const Coordinator *coordinator, const std::string &path);
+    AST(const Coordinator* coordinator, const Hash* fileHash);
 
     bool setPackage(const char *package);
     bool addImport(const char *import);
@@ -50,14 +55,17 @@
     bool isInterface() const;
     bool containsInterfaces() const;
 
-    // Returns true iff successful.
-    bool addTypeDef(const char* localName, Type* type, const Location& location,
-                    std::string* errorMsg, Scope* scope);
+    // Adds package, version and scope stack to local name
+    FQName makeFullName(const char* localName, Scope* scope) const;
 
-    // Returns true iff successful.
-    bool addScopedType(NamedType* type, std::string* errorMsg, Scope* scope);
+    void addScopedType(NamedType* type, Scope* scope);
 
-    const std::string &getFilename() const;
+    const std::string& getFilename() const;
+    const Hash* getFileHash() const;
+
+    // Look up local identifier.
+    // It could be plain identifier or enum value as described by lookupEnumValue.
+    LocalIdentifier* lookupLocalIdentifier(const Reference<LocalIdentifier>& ref, Scope* scope);
 
     // Look up an enum value by "FQName:valueName".
     EnumValue* lookupEnumValue(const FQName& fqName, std::string* errorMsg, Scope* scope);
@@ -69,20 +77,66 @@
 
     void addImportedAST(AST *ast);
 
-    status_t generateCpp(const std::string &outputPath) const;
-    status_t generateCppHeaders(const std::string &outputPath) const;
-    status_t generateCppSources(const std::string &outputPath) const;
-    status_t generateCppImpl(const std::string &outputPath) const;
-    status_t generateStubImplHeader(const std::string& outputPath) const;
-    status_t generateStubImplSource(const std::string& outputPath) const;
+    // Calls all passes after parsing required before
+    // being ready to generate output.
+    status_t postParse();
 
-    status_t generateJava(
-            const std::string &outputPath,
-            const std::string &limitToType) const;
+    // Recursive pass on constant expression tree
+    status_t constantExpressionRecursivePass(
+        const std::function<status_t(ConstantExpression*)>& func, bool processBeforeDependencies);
 
-    status_t generateJavaTypes(
-            const std::string &outputPath,
-            const std::string &limitToType) const;
+    // Recursive tree pass that looks up all referenced types
+    status_t lookupTypes();
+
+    // Recursive tree pass that looks up all referenced local identifiers
+    status_t lookupLocalIdentifiers();
+
+    // Recursive tree pass that validates that all defined types
+    // have unique names in their scopes.
+    status_t validateDefinedTypesUniqueNames() const;
+
+    // Recursive tree pass that completes type declarations
+    // that depend on super types
+    status_t resolveInheritance();
+
+    // Recursive tree pass that evaluates constant expressions
+    status_t evaluate();
+
+    // Recursive tree pass that validates all type-related
+    // syntax restrictions
+    status_t validate() const;
+
+    // Recursive tree pass that ensures that type definitions and references
+    // are acyclic and reorderes type definitions in reversed topological order.
+    status_t topologicalReorder();
+
+    // Recursive tree pass that ensures that constant expressions
+    // are acyclic.
+    status_t checkAcyclicConstantExpressions() const;
+
+    // Recursive tree pass that checks C++ forward declaration restrictions.
+    status_t checkForwardReferenceRestrictions() const;
+
+    status_t gatherReferencedTypes();
+
+    void generateCppSource(Formatter& out) const;
+
+    void generateInterfaceHeader(Formatter& out) const;
+    void generateHwBinderHeader(Formatter& out) const;
+    void generateStubHeader(Formatter& out) const;
+    void generateProxyHeader(Formatter& out) const;
+    void generatePassthroughHeader(Formatter& out) const;
+
+    void generateCppImplHeader(Formatter& out) const;
+    void generateCppImplSource(Formatter& out) const;
+
+    void generateCppAdapterHeader(Formatter& out) const;
+    void generateCppAdapterSource(Formatter& out) const;
+
+    void generateJava(Formatter& out, const std::string& limitToType) const;
+    void generateJavaTypes(Formatter& out, const std::string& limitToType) const;
+
+    void generateVts(Formatter& out) const;
 
     void getImportedPackages(std::set<FQName> *importSet) const;
 
@@ -90,19 +144,25 @@
     // each AST in each package referenced in importSet.
     void getImportedPackagesHierarchy(std::set<FQName> *importSet) const;
 
-    status_t generateVts(const std::string &outputPath) const;
-
     bool isJavaCompatible() const;
 
-    // Return the set of FQNames for those interfaces and types that are
-    // actually referenced in the AST, not merely imported.
-    const std::set<FQName>& getImportedNames() const {
-        return mImportedNames;
-    }
-
-    // Get transitive closure of imported interface/types.
+    // Warning: this only includes names explicitly referenced in code.
+    //   It does not include all names which are imported.
+    //
+    // Currently, there is one valid usecase for this: importing exactly
+    // the names which need to be imported in generated code. If you import
+    // based on getAllImportedNamesGranular instead, you will import things
+    // that aren't actually used in the resultant code.
+    //
+    // Get transitive closure of imported interface/types. This will add
+    // everything exported by a package even if only a single type from
+    // that package was explicitly imported!
     void getAllImportedNames(std::set<FQName> *allImportSet) const;
 
+    // Get imported types, this includes those explicitly imported as well
+    // as all types defined in imported packages.
+    void getAllImportedNamesGranular(std::set<FQName> *allImportSet) const;
+
     void appendToExportedTypesVector(
             std::vector<const Type *> *exportedTypes) const;
 
@@ -120,9 +180,17 @@
 
     Scope* getRootScope();
 
+    static void generateCppPackageInclude(Formatter& out, const FQName& package,
+                                          const std::string& klass);
+
+    void addDefinedTypes(std::set<FQName> *definedTypes) const;
+    void addReferencedTypes(std::set<FQName> *referencedTypes) const;
+
+    void addToImportedNamesGranular(const FQName &fqName);
+
    private:
-    const Coordinator *mCoordinator;
-    std::string mPath;
+    const Coordinator* mCoordinator;
+    const Hash* mFileHash;
 
     RootScope mRootScope;
 
@@ -130,8 +198,16 @@
 
     // A set of all external interfaces/types that are _actually_ referenced
     // in this AST, this is a subset of those specified in import statements.
+    // Note that this set only resolves to the granularity of either an
+    // interface type or a whole package.
     std::set<FQName> mImportedNames;
 
+    // This is the set of actually imported types.
+    std::set<FQName> mImportedNamesGranular;
+
+    // Warning: this only includes names explicitly referenced in code.
+    //   It does not include all names which are imported.
+    //
     // A set of all ASTs we explicitly or implicitly (types.hal) import.
     std::set<AST *> mImportedASTs;
 
@@ -147,7 +223,7 @@
     // used by the parser.
     size_t mSyntaxErrors = 0;
 
-    bool addScopedTypeInternal(NamedType* type, std::string* errorMsg, Scope* scope);
+    std::set<FQName> mReferencedTypeNames;
 
     // Helper functions for lookupType.
     Type* lookupTypeLocally(const FQName& fqName, Scope* scope);
@@ -164,65 +240,47 @@
     void getPackageAndVersionComponents(
             std::vector<std::string> *components, bool cpp_compatible) const;
 
-    static void generateCppPackageInclude(
-            Formatter &out,
-            const FQName &package,
-            const std::string &klass);
-
     std::string makeHeaderGuard(const std::string &baseName,
                                 bool indicateGenerated = true) const;
     void enterLeaveNamespace(Formatter &out, bool enter) const;
 
     static void generateCheckNonNull(Formatter &out, const std::string &nonNull);
 
-    status_t generateInterfaceHeader(const std::string &outputPath) const;
-    status_t generateHwBinderHeader(const std::string &outputPath) const;
-    status_t generateStubHeader(const std::string &outputPath) const;
-    status_t generateProxyHeader(const std::string &outputPath) const;
-    status_t generatePassthroughHeader(const std::string &outputPath) const;
-
-    status_t generateTypeSource(
-            Formatter &out, const std::string &ifaceName) const;
+    void generateTypeSource(Formatter& out, const std::string& ifaceName) const;
 
     // a method, and in which interface is it originally defined.
     // be careful of the case where method.isHidlReserved(), where interface
     // is effectively useless.
-    using MethodGenerator = std::function<status_t(const Method *, const Interface *)>;
+    using MethodGenerator = std::function<void(const Method*, const Interface*)>;
 
     void generateTemplatizationLink(Formatter& out) const;
+    void generateCppTag(Formatter& out, const std::string& tag) const;
 
-    status_t generateMethods(Formatter &out, MethodGenerator gen, bool includeParents = true) const;
-    status_t generateStubImplMethod(Formatter &out,
-                                    const std::string &className,
-                                    const Method *method) const;
-    status_t generatePassthroughMethod(Formatter &out,
-                                       const Method *method) const;
-    status_t generateStaticProxyMethodSource(Formatter &out,
-                                             const std::string &className,
-                                             const Method *method) const;
-    status_t generateProxyMethodSource(Formatter &out,
-                                       const std::string &className,
-                                       const Method *method,
-                                       const Interface *superInterface) const;
+    void generateMethods(Formatter& out, const MethodGenerator& gen,
+                         bool includeParents = true) const;
+    void generateStubImplMethod(Formatter& out, const std::string& className,
+                                const Method* method) const;
+    void generatePassthroughMethod(Formatter& out, const Method* method) const;
+    void generateStaticProxyMethodSource(Formatter& out, const std::string& className,
+                                         const Method* method) const;
+    void generateProxyMethodSource(Formatter& out, const std::string& className,
+                                   const Method* method, const Interface* superInterface) const;
+    void generateAdapterMethod(Formatter& out, const Method* method) const;
 
     void generateFetchSymbol(Formatter &out, const std::string &ifaceName) const;
 
-    status_t generateProxySource(
-            Formatter &out, const FQName &fqName) const;
+    void generateProxySource(Formatter& out, const FQName& fqName) const;
 
-    status_t generateStubSource(
-            Formatter &out, const Interface *iface) const;
+    void generateStubSource(Formatter& out, const Interface* iface) const;
 
-    status_t generateStubSourceForMethod(Formatter &out,
-                                         const Method *method,
-                                         const Interface *superInterface) const;
-    status_t generateStaticStubMethodSource(Formatter &out,
-                                            const std::string &className,
-                                            const Method *method) const;
+    void generateStubSourceForMethod(Formatter& out, const Method* method,
+                                     const Interface* superInterface) const;
+    void generateStaticStubMethodSource(Formatter& out, const FQName& fqName,
+                                        const Method* method) const;
 
-    status_t generatePassthroughSource(Formatter &out) const;
+    void generatePassthroughSource(Formatter& out) const;
 
-    status_t generateInterfaceSource(Formatter &out) const;
+    void generateInterfaceSource(Formatter& out) const;
 
     enum InstrumentationEvent {
         SERVER_API_ENTRY = 0,
@@ -247,39 +305,24 @@
             InstrumentationEvent event,
             const Method *method) const;
 
-    void declareCppReaderLocals(
-            Formatter &out,
-            const std::vector<TypedVar *> &arg,
-            bool forResults) const;
+    void declareCppReaderLocals(Formatter& out, const std::vector<NamedReference<Type>*>& arg,
+                                bool forResults) const;
 
-    void emitCppReaderWriter(
-            Formatter &out,
-            const std::string &parcelObj,
-            bool parcelObjIsPointer,
-            const TypedVar *arg,
-            bool isReader,
-            Type::ErrorMode mode,
-            bool addPrefixToName) const;
+    void emitCppReaderWriter(Formatter& out, const std::string& parcelObj, bool parcelObjIsPointer,
+                             const NamedReference<Type>* arg, bool isReader, Type::ErrorMode mode,
+                             bool addPrefixToName) const;
 
-    void emitCppResolveReferences(
-            Formatter &out,
-            const std::string &parcelObj,
-            bool parcelObjIsPointer,
-            const TypedVar *arg,
-            bool isReader,
-            Type::ErrorMode mode,
-            bool addPrefixToName) const;
+    void emitCppResolveReferences(Formatter& out, const std::string& parcelObj,
+                                  bool parcelObjIsPointer, const NamedReference<Type>* arg,
+                                  bool isReader, Type::ErrorMode mode, bool addPrefixToName) const;
 
-    void emitJavaReaderWriter(
-            Formatter &out,
-            const std::string &parcelObj,
-            const TypedVar *arg,
-            bool isReader,
-            bool addPrefixToName) const;
+    void emitJavaReaderWriter(Formatter& out, const std::string& parcelObj,
+                              const NamedReference<Type>* arg, bool isReader,
+                              bool addPrefixToName) const;
 
-    status_t emitTypeDeclarations(Formatter &out) const;
-    status_t emitJavaTypeDeclarations(Formatter &out) const;
-    status_t emitVtsTypeDeclarations(Formatter &out) const;
+    void emitTypeDeclarations(Formatter& out) const;
+    void emitJavaTypeDeclarations(Formatter& out) const;
+    void emitVtsTypeDeclarations(Formatter& out) const;
 
     DISALLOW_COPY_AND_ASSIGN(AST);
 };
diff --git a/Android.bp b/Android.bp
index ef388fc..dd51705 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,18 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-subdirs = [
-    "c2hal",
-    "test",
-    "utils",
-]
-
-hidl_flags = [
-    "-O0",
-    "-g",
-    "-Wall",
-    "-Werror",
-]
+cc_defaults {
+    name: "hidl-gen-defaults",
+    cpp_std: "experimental",
+    cflags: [
+        "-O0",
+        "-g",
+        "-Wall",
+        "-Werror",
+    ],
+}
 
 // This configuration is inherited by all hidl-gen-generated modules.
 cc_defaults {
@@ -31,13 +29,22 @@
     cflags: [
         "-Wall",
         "-Werror",
+        "-Wextra-semi",
     ],
     product_variables: {
         debuggable: {
             cflags: ["-D__ANDROID_DEBUGGABLE__"]
         },
-        treble: {
-            cflags: ["-D__ANDROID_TREBLE__"]
+    },
+}
+
+// This configuration is inherited by all hidl-gen-java modules
+java_defaults {
+    name: "hidl-java-module-defaults",
+    // TODO(b/68433855): allow HIDL java to build in the PDK
+    product_variables: {
+        pdk: {
+            enabled: false,
         },
     },
 }
@@ -48,7 +55,7 @@
 cc_library {
     name: "libhidl-gen-hash",
     host_supported: true,
-    cflags: hidl_flags,
+    defaults: ["hidl-gen-defaults"],
     srcs: ["Hash.cpp"],
     local_include_dirs: ["include_hash/hidl-hash"],
     export_include_dirs: ["include_hash"],
@@ -64,17 +71,19 @@
 //
 cc_library_host_shared {
     name: "libhidl-gen",
-    cflags: hidl_flags,
+    defaults: ["hidl-gen-defaults"],
     srcs: [
         "Annotation.cpp",
         "ArrayType.cpp",
         "CompoundType.cpp",
         "ConstantExpression.cpp",
         "DeathRecipientType.cpp",
+        "DocComment.cpp",
         "EnumType.cpp",
         "HandleType.cpp",
         "HidlTypeAssertion.cpp",
         "Interface.cpp",
+        "Location.cpp",
         "MemoryType.cpp",
         "Method.cpp",
         "NamedType.cpp",
@@ -94,6 +103,11 @@
         "libhidl-gen-hash",
         "libhidl-gen-utils",
     ],
+    export_shared_lib_headers: [
+        "libbase",
+        "libhidl-gen-utils",
+    ],
+    export_include_dirs: ["."], // for tests
 }
 
 //
@@ -102,10 +116,11 @@
 
 cc_library_host_shared {
     name: "libhidl-gen-ast",
-    cflags: hidl_flags,
+    defaults: ["hidl-gen-defaults"],
     srcs: [
         "Coordinator.cpp",
         "generateCpp.cpp",
+        "generateCppAdapter.cpp",
         "generateCppImpl.cpp",
         "generateJava.cpp",
         "generateVts.cpp",
@@ -120,6 +135,11 @@
         "libhidl-gen-hash",
         "libhidl-gen-utils",
     ],
+    export_shared_lib_headers: [
+        "libbase",
+        "libhidl-gen-utils",
+    ],
+    export_include_dirs: ["."], // for tests
 }
 
 //
@@ -127,7 +147,7 @@
 //
 cc_binary_host {
     name: "hidl-gen",
-    cflags: hidl_flags,
+    defaults: ["hidl-gen-defaults"],
     srcs: ["main.cpp"],
     shared_libs: [
         "libbase",
diff --git a/Annotation.cpp b/Annotation.cpp
index a3e49f8..e5a66b0 100644
--- a/Annotation.cpp
+++ b/Annotation.cpp
@@ -19,35 +19,27 @@
 #include <android-base/logging.h>
 #include <hidl-util/Formatter.h>
 #include <hidl-util/StringHelper.h>
+#include <algorithm>
 #include <vector>
 
 namespace android {
 
+AnnotationParam::AnnotationParam(const std::string& name) : mName(name) {}
 
-AnnotationParam::AnnotationParam(const std::string &name,
-                std::vector<std::string> *values)
-: mName(name), mValues(values) {}
-
-AnnotationParam::AnnotationParam(const std::string &name,
-                std::vector<ConstantExpression *> *values)
-        : mName(name) {
-    mValues = new std::vector<std::string>();
-    for(ConstantExpression *ce : *values) {
-        mValues->push_back(ce->value() + " /* " + ce->description() + " */");
-    }
-}
-
-const std::string &AnnotationParam::getName() const {
+const std::string& AnnotationParam::getName() const {
     return mName;
 }
 
-const std::vector<std::string> *AnnotationParam::getValues() const {
-    return mValues;
+std::vector<ConstantExpression*> AnnotationParam::getConstantExpressions() {
+    const auto& constRet = static_cast<const AnnotationParam*>(this)->getConstantExpressions();
+    std::vector<ConstantExpression*> ret(constRet.size());
+    std::transform(constRet.begin(), constRet.end(), ret.begin(),
+                   [](const auto* ce) { return const_cast<ConstantExpression*>(ce); });
+    return ret;
 }
 
-const std::string &AnnotationParam::getSingleValue() const {
-    CHECK_EQ(mValues->size(), 1u) << mName << " requires one values but has multiple";
-    return mValues->at(0);
+std::vector<const ConstantExpression*> AnnotationParam::getConstantExpressions() const {
+    return {};
 }
 
 std::string AnnotationParam::getSingleString() const {
@@ -75,11 +67,53 @@
     return false;
 }
 
-Annotation::Annotation(const char *name,AnnotationParamVector *params)
-        : mName(name),
-          mParams(params) {
+StringAnnotationParam::StringAnnotationParam(const std::string& name,
+                                             std::vector<std::string>* values)
+    : AnnotationParam(name), mValues(values) {}
+
+std::vector<std::string> StringAnnotationParam::getValues() const {
+    return *mValues;
 }
 
+std::string StringAnnotationParam::getSingleValue() const {
+    CHECK_EQ(mValues->size(), 1u) << mName << " requires one value but has multiple";
+    return mValues->at(0);
+}
+
+ConstantExpressionAnnotationParam::ConstantExpressionAnnotationParam(
+    const std::string& name, std::vector<ConstantExpression*>* values)
+    : AnnotationParam(name), mValues(values) {}
+
+std::string convertToString(const ConstantExpression* value) {
+    if (value->descriptionIsTrivial()) {
+        return value->value();
+    }
+    return value->value() + " /* " + value->description() + " */";
+}
+
+std::vector<std::string> ConstantExpressionAnnotationParam::getValues() const {
+    std::vector<std::string> ret;
+    for (const auto* value : *mValues) {
+        ret.push_back(convertToString(value));
+    };
+    return ret;
+}
+
+std::string ConstantExpressionAnnotationParam::getSingleValue() const {
+    CHECK_EQ(mValues->size(), 1u) << mName << " requires one value but has multiple";
+    return convertToString(mValues->at(0));
+}
+
+std::vector<const ConstantExpression*> ConstantExpressionAnnotationParam::getConstantExpressions()
+    const {
+    std::vector<const ConstantExpression*> ret;
+    ret.insert(ret.end(), mValues->begin(), mValues->end());
+    return ret;
+}
+
+Annotation::Annotation(const char* name, AnnotationParamVector* params)
+    : mName(name), mParams(params) {}
+
 std::string Annotation::name() const {
     return mName;
 }
@@ -89,7 +123,7 @@
 }
 
 const AnnotationParam *Annotation::getParam(const std::string &name) const {
-    for (auto *i: *mParams) {
+    for (const auto* i : *mParams) {
         if (i->getName() == name) {
             return i;
         }
@@ -98,6 +132,23 @@
     return nullptr;
 }
 
+std::vector<ConstantExpression*> Annotation::getConstantExpressions() {
+    const auto& constRet = static_cast<const Annotation*>(this)->getConstantExpressions();
+    std::vector<ConstantExpression*> ret(constRet.size());
+    std::transform(constRet.begin(), constRet.end(), ret.begin(),
+                   [](const auto* ce) { return const_cast<ConstantExpression*>(ce); });
+    return ret;
+}
+
+std::vector<const ConstantExpression*> Annotation::getConstantExpressions() const {
+    std::vector<const ConstantExpression*> ret;
+    for (const auto* param : *mParams) {
+        const auto& retParam = param->getConstantExpressions();
+        ret.insert(ret.end(), retParam.begin(), retParam.end());
+    }
+    return ret;
+}
+
 void Annotation::dump(Formatter &out) const {
     out << "@" << mName;
 
@@ -116,14 +167,14 @@
 
         out << param->getName() << "=";
 
-        const std::vector<std::string> *values = param->getValues();
-        if (values->size() > 1) {
+        const std::vector<std::string>& values = param->getValues();
+        if (values.size() > 1) {
             out << "{";
         }
 
-        out << StringHelper::JoinStrings(*values, ", ");
+        out << StringHelper::JoinStrings(values, ", ");
 
-        if (values->size() > 1) {
+        if (values.size() > 1) {
             out << "}";
         }
     }
diff --git a/Annotation.h b/Annotation.h
index 18d9ab0..dfeb59e 100644
--- a/Annotation.h
+++ b/Annotation.h
@@ -21,6 +21,7 @@
 #include <android-base/macros.h>
 #include <map>
 #include <string>
+#include <vector>
 
 #include "ConstantExpression.h"
 
@@ -29,15 +30,12 @@
 struct Formatter;
 
 struct AnnotationParam {
-    AnnotationParam(const std::string &name,
-                    std::vector<std::string> *values);
-    AnnotationParam(const std::string &name,
-                    std::vector<ConstantExpression *> *values);
+    virtual ~AnnotationParam() {}
 
-    const std::string &getName() const;
-    const std::vector<std::string> *getValues() const;
+    const std::string& getName() const;
 
-    const std::string &getSingleValue() const;
+    virtual std::vector<std::string> getValues() const = 0;
+    virtual std::string getSingleValue() const = 0;
 
     /* Returns unquoted version of getSingleValue */
     std::string getSingleString() const;
@@ -45,12 +43,39 @@
     /* Returns value interpretted as a boolean */
     bool getSingleBool() const;
 
-private:
+    std::vector<ConstantExpression*> getConstantExpressions();
+    virtual std::vector<const ConstantExpression*> getConstantExpressions() const;
+
+   protected:
     const std::string mName;
-    std::vector<std::string> *mValues;
+
+    AnnotationParam(const std::string& name);
 };
 
-using AnnotationParamVector = std::vector<const AnnotationParam*>;
+struct StringAnnotationParam : AnnotationParam {
+    StringAnnotationParam(const std::string& name, std::vector<std::string>* values);
+
+    std::vector<std::string> getValues() const override;
+    std::string getSingleValue() const override;
+
+   private:
+    std::vector<std::string>* const mValues;
+};
+
+struct ConstantExpressionAnnotationParam : AnnotationParam {
+    ConstantExpressionAnnotationParam(const std::string& name,
+                                      std::vector<ConstantExpression*>* values);
+
+    std::vector<std::string> getValues() const override;
+    std::string getSingleValue() const override;
+
+    std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+   private:
+    std::vector<ConstantExpression*>* const mValues;
+};
+
+using AnnotationParamVector = std::vector<AnnotationParam*>;
 
 struct Annotation {
     Annotation(const char *name, AnnotationParamVector *params);
@@ -59,9 +84,12 @@
     const AnnotationParamVector &params() const;
     const AnnotationParam *getParam(const std::string &name) const;
 
+    std::vector<ConstantExpression*> getConstantExpressions();
+    std::vector<const ConstantExpression*> getConstantExpressions() const;
+
     void dump(Formatter &out) const;
 
-private:
+   private:
     std::string mName;
     AnnotationParamVector *mParams;
 
diff --git a/ArrayType.cpp b/ArrayType.cpp
index 382665b..5155a27 100644
--- a/ArrayType.cpp
+++ b/ArrayType.cpp
@@ -16,26 +16,17 @@
 
 #include "ArrayType.h"
 
-#include <hidl-util/Formatter.h>
 #include <android-base/logging.h>
+#include <hidl-util/Formatter.h>
+#include <iostream>
 
 #include "ConstantExpression.h"
 
 namespace android {
 
-ArrayType::ArrayType(ArrayType *srcArray, ConstantExpression *size)
-    : mElementType(srcArray->mElementType),
-      mSizes(srcArray->mSizes) {
-    prependDimension(size);
-}
-
-ArrayType::ArrayType(Type *elementType, ConstantExpression *size)
-    : mElementType(elementType) {
-    prependDimension(size);
-}
-
-void ArrayType::prependDimension(ConstantExpression *size) {
-    mSizes.insert(mSizes.begin(), size);
+ArrayType::ArrayType(const Reference<Type>& elementType, ConstantExpression* size, Scope* parent)
+    : Type(parent), mElementType(elementType), mSizes{size} {
+    CHECK(!elementType.isEmptyReference());
 }
 
 void ArrayType::appendDimension(ConstantExpression *size) {
@@ -50,12 +41,12 @@
     return true;
 }
 
-bool ArrayType::canCheckEquality() const {
-    return mElementType->canCheckEquality();
+bool ArrayType::deepCanCheckEquality(std::unordered_set<const Type*>* visited) const {
+    return mElementType->canCheckEquality(visited);
 }
 
-Type *ArrayType::getElementType() const {
-    return mElementType;
+const Type* ArrayType::getElementType() const {
+    return mElementType.get();
 }
 
 std::string ArrayType::typeName() const {
@@ -66,6 +57,38 @@
     return std::to_string(dimension()) + "d array of " + mElementType->typeName();
 }
 
+std::vector<const Reference<Type>*> ArrayType::getReferences() const {
+    return {&mElementType};
+}
+
+std::vector<const ConstantExpression*> ArrayType::getConstantExpressions() const {
+    std::vector<const ConstantExpression*> ret;
+    ret.insert(ret.end(), mSizes.begin(), mSizes.end());
+    return ret;
+}
+
+status_t ArrayType::resolveInheritance() {
+    // Resolve for typedefs
+    while (mElementType->isArray()) {
+        ArrayType* innerArray = static_cast<ArrayType*>(mElementType.get());
+        mSizes.insert(mSizes.end(), innerArray->mSizes.begin(), innerArray->mSizes.end());
+        mElementType = innerArray->mElementType;
+    }
+    return Type::resolveInheritance();
+}
+
+status_t ArrayType::validate() const {
+    CHECK(!mElementType->isArray());
+
+    if (mElementType->isBinder()) {
+        std::cerr << "ERROR: Arrays of interface types are not supported"
+                  << " at " << mElementType.location() << "\n";
+
+        return UNKNOWN_ERROR;
+    }
+    return Type::validate();
+}
+
 std::string ArrayType::getCppType(StorageMode mode,
                                   bool specifyNamespaces) const {
     const std::string base = mElementType->getCppStackType(specifyNamespaces);
@@ -351,8 +374,11 @@
     return mElementType->needsEmbeddedReadWrite();
 }
 
-bool ArrayType::needsResolveReferences() const {
-    return mElementType->needsResolveReferences();
+bool ArrayType::deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const {
+    if (mElementType->needsResolveReferences(visited)) {
+        return true;
+    }
+    return Type::deepNeedsResolveReferences(visited);
 }
 
 bool ArrayType::resultNeedsDeref() const {
@@ -434,8 +460,16 @@
     std::string offsetName = "_hidl_array_offset_" + std::to_string(depth);
     out << "long " << offsetName << " = " << offset << ";\n";
 
+    const bool isPrimitiveArray = mElementType->isScalar();
+
+    /* If the element type corresponds to a Java primitive type we can optimize
+       the innermost loop by copying a linear range of memory instead of doing
+       a per-element copy. As a result the outer nested loop does not include
+       the final dimension. */
+    const size_t loopDimensions = mSizes.size() - (isPrimitiveArray ? 1 : 0);
+
     std::string indexString;
-    for (size_t dim = 0; dim < mSizes.size(); ++dim) {
+    for (size_t dim = 0; dim < loopDimensions; ++dim) {
         std::string iteratorName =
             "_hidl_index_" + std::to_string(depth) + "_" + std::to_string(dim);
 
@@ -465,21 +499,57 @@
             << "();\n";
     }
 
-    mElementType->emitJavaFieldReaderWriter(
-            out,
-            depth + 1,
-            parcelName,
-            blobName,
-            fieldName + indexString,
-            offsetName,
-            isReader);
+    if (!isPrimitiveArray) {
+        mElementType->emitJavaFieldReaderWriter(
+                out,
+                depth + 1,
+                parcelName,
+                blobName,
+                fieldName + indexString,
+                offsetName,
+                isReader);
 
-    size_t elementAlign, elementSize;
-    mElementType->getAlignmentAndSize(&elementAlign, &elementSize);
+        size_t elementAlign, elementSize;
+        mElementType->getAlignmentAndSize(&elementAlign, &elementSize);
 
-    out << offsetName << " += " << std::to_string(elementSize) << ";\n";
+        out << offsetName << " += " << std::to_string(elementSize) << ";\n";
+    } else {
+        if (isReader) {
+            out << blobName
+                << ".copyTo"
+                << mElementType->getJavaSuffix()
+                << "Array("
+                << offsetName
+                << ", "
+                << fieldName
+                << indexString
+                << ", "
+                << mSizes.back()->javaValue()
+                << " /* size */);\n";
+        } else {
+            out << blobName
+                << ".put"
+                << mElementType->getJavaSuffix()
+                << "Array("
+                << offsetName
+                << ", "
+                << fieldName
+                << indexString
+                << ");\n";
+        }
 
-    for (size_t dim = 0; dim < mSizes.size(); ++dim) {
+        size_t elementAlign, elementSize;
+        mElementType->getAlignmentAndSize(&elementAlign, &elementSize);
+
+        out << offsetName
+            << " += "
+            << mSizes.back()->javaValue()
+            << " * "
+            << elementSize
+            << ";\n";
+    }
+
+    for (size_t dim = 0; dim < loopDimensions; ++dim) {
         out.unindent();
         out << "}\n";
     }
@@ -488,17 +558,14 @@
     out << "}\n";
 }
 
-status_t ArrayType::emitVtsTypeDeclarations(Formatter &out) const {
+void ArrayType::emitVtsTypeDeclarations(Formatter& out) const {
     out << "type: " << getVtsType() << "\n";
     out << "vector_size: " << mSizes[0]->value() << "\n";
     out << "vector_value: {\n";
     out.indent();
     // Simple array case.
     if (mSizes.size() == 1) {
-        status_t err = mElementType->emitVtsTypeDeclarations(out);
-        if (err != OK) {
-            return err;
-        }
+        mElementType->emitVtsTypeDeclarations(out);
     } else {  // Multi-dimension array case.
         for (size_t index = 1; index < mSizes.size(); index++) {
             out << "type: " << getVtsType() << "\n";
@@ -506,10 +573,7 @@
             out << "vector_value: {\n";
             out.indent();
             if (index == mSizes.size() - 1) {
-                status_t err = mElementType->emitVtsTypeDeclarations(out);
-                if (err != OK) {
-                    return err;
-                }
+                mElementType->emitVtsTypeDeclarations(out);
             }
         }
     }
@@ -517,15 +581,20 @@
         out.unindent();
         out << "}\n";
     }
-    return OK;
 }
 
-bool ArrayType::isJavaCompatible() const {
-    return mElementType->isJavaCompatible();
+bool ArrayType::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
+    if (!mElementType->isJavaCompatible(visited)) {
+        return false;
+    }
+    return Type::deepIsJavaCompatible(visited);
 }
 
-bool ArrayType::containsPointer() const {
-    return mElementType->containsPointer();
+bool ArrayType::deepContainsPointer(std::unordered_set<const Type*>* visited) const {
+    if (mElementType->containsPointer(visited)) {
+        return true;
+    }
+    return Type::deepContainsPointer(visited);
 }
 
 void ArrayType::getAlignmentAndSize(size_t *align, size_t *size) const {
diff --git a/ArrayType.h b/ArrayType.h
index 05ba783..2c13a33 100644
--- a/ArrayType.h
+++ b/ArrayType.h
@@ -18,6 +18,7 @@
 
 #define ARRAY_TYPE_H_
 
+#include "Reference.h"
 #include "Type.h"
 
 #include <vector>
@@ -27,22 +28,27 @@
 struct ConstantExpression;
 
 struct ArrayType : public Type {
-    // Extends existing array by adding another dimension.
-    ArrayType(ArrayType *srcArray, ConstantExpression *size);
-
-    ArrayType(Type *elementType, ConstantExpression *size);
+    ArrayType(const Reference<Type>& elementType, ConstantExpression* size, Scope* parent);
 
     bool isArray() const override;
-    bool canCheckEquality() const override;
+    bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
 
-    Type *getElementType() const;
+    const Type* getElementType() const;
 
-    void prependDimension(ConstantExpression *size);
     void appendDimension(ConstantExpression *size);
     size_t countDimensions() const;
 
     std::string typeName() const override;
 
+    std::vector<const Reference<Type>*> getReferences() const override;
+
+    std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+    // Extends existing array by adding another dimension.
+    status_t resolveInheritance() override;
+
+    status_t validate() const override;
+
     std::string getCppType(StorageMode mode,
                            bool specifyNamespaces) const override;
 
@@ -103,7 +109,7 @@
             const std::string &name) const override;
 
     bool needsEmbeddedReadWrite() const override;
-    bool needsResolveReferences() const override;
+    bool deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const override;
     bool resultNeedsDeref() const override;
 
     void emitJavaReaderWriter(
@@ -124,16 +130,16 @@
             const std::string &offset,
             bool isReader) const override;
 
-    status_t emitVtsTypeDeclarations(Formatter &out) const override;
+    void emitVtsTypeDeclarations(Formatter& out) const override;
 
-    bool isJavaCompatible() const override;
-    bool containsPointer() const override;
+    bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
+    bool deepContainsPointer(std::unordered_set<const Type*>* visited) const override;
 
     void getAlignmentAndSize(size_t *align, size_t *size) const override;
 
-private:
-    Type *mElementType;
-    std::vector<ConstantExpression *> mSizes;
+   private:
+    Reference<Type> mElementType;
+    std::vector<ConstantExpression*> mSizes;
 
     size_t dimension() const;
 
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..ebc0bad
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,47 @@
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+$(call add-clean-step, rm -rf $(SOONG_OUT_DIR)/.intermediates/hardware/interfaces/tests/extension)
diff --git a/CompoundType.cpp b/CompoundType.cpp
index b138d7b..87f0f14 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -18,61 +18,82 @@
 
 #include "ArrayType.h"
 #include "VectorType.h"
-#include <hidl-util/Formatter.h>
+
 #include <android-base/logging.h>
+#include <hidl-util/Formatter.h>
+#include <iostream>
+#include <unordered_set>
 
 namespace android {
 
-CompoundType::CompoundType(Style style, const char* localName, const Location& location,
-                           Scope* parent)
-    : Scope(localName, location, parent), mStyle(style), mFields(NULL) {}
+CompoundType::CompoundType(Style style, const char* localName, const FQName& fullName,
+                           const Location& location, Scope* parent)
+    : Scope(localName, fullName, location, parent), mStyle(style), mFields(NULL) {}
 
 CompoundType::Style CompoundType::style() const {
     return mStyle;
 }
 
-bool CompoundType::setFields(
-        std::vector<CompoundField *> *fields, std::string *errorMsg) {
+void CompoundType::setFields(std::vector<NamedReference<Type>*>* fields) {
     mFields = fields;
+}
 
-    for (const auto &field : *fields) {
-        const Type &type = field->type();
+std::vector<const Reference<Type>*> CompoundType::getReferences() const {
+    std::vector<const Reference<Type>*> ret;
+    ret.insert(ret.begin(), mFields->begin(), mFields->end());
+    return ret;
+}
 
-        if (type.isBinder()
-                || (type.isVector()
-                    && static_cast<const VectorType *>(
-                        &type)->isVectorOfBinders())) {
-            *errorMsg =
-                "Structs/Unions must not contain references to interfaces.";
+status_t CompoundType::validate() const {
+    for (const auto* field : *mFields) {
+        const Type& type = field->type();
 
-            return false;
+        if ((type.isVector() && static_cast<const VectorType*>(&type)->isVectorOfBinders())) {
+            std::cerr << "ERROR: Struct/Union must not contain references to interfaces at "
+                      << field->location() << "\n";
+            return UNKNOWN_ERROR;
         }
 
         if (mStyle == STYLE_UNION) {
             if (type.needsEmbeddedReadWrite()) {
-                // Can't have those in a union.
-
-                *errorMsg =
-                    "Unions must not contain any types that need fixup.";
-
-                return false;
+                std::cerr << "ERROR: Union must not contain any types that need fixup at "
+                          << field->location() << "\n";
+                return UNKNOWN_ERROR;
             }
         }
     }
 
-    return true;
+    status_t err = validateUniqueNames();
+    if (err != OK) return err;
+
+    return Scope::validate();
+}
+
+status_t CompoundType::validateUniqueNames() const {
+    std::unordered_set<std::string> names;
+
+    for (const auto* field : *mFields) {
+        if (names.find(field->name()) != names.end()) {
+            std::cerr << "ERROR: Redefinition of field '" << field->name() << "' at "
+                      << field->location() << "\n";
+            return UNKNOWN_ERROR;
+        }
+        names.insert(field->name());
+    }
+
+    return OK;
 }
 
 bool CompoundType::isCompoundType() const {
     return true;
 }
 
-bool CompoundType::canCheckEquality() const {
+bool CompoundType::deepCanCheckEquality(std::unordered_set<const Type*>* visited) const {
     if (mStyle == STYLE_UNION) {
         return false;
     }
-    for (const auto &field : *mFields) {
-        if (!field->type().canCheckEquality()) {
+    for (const auto* field : *mFields) {
+        if (!field->get()->canCheckEquality(visited)) {
             return false;
         }
     }
@@ -104,7 +125,7 @@
             return "const " + base + "&";
 
         case StorageMode_Result:
-            return "const " + base + "*";
+            return base + (containsInterface()?"":"*");
     }
 }
 
@@ -126,6 +147,22 @@
     CHECK(!"Should not be here");
 }
 
+bool CompoundType::containsInterface() const {
+    for (const auto& field : *mFields) {
+        if (field->type().isCompoundType()) {
+            const Type& t = field->type();
+            const CompoundType* ct = static_cast<const CompoundType*>(&t);
+            if (ct->containsInterface()) {
+                return true;
+            }
+        }
+        if (field->type().isInterface()) {
+            return true;
+        }
+    }
+    return false;
+}
+
 void CompoundType::emitReaderWriter(
         Formatter &out,
         const std::string &name,
@@ -133,57 +170,47 @@
         bool parcelObjIsPointer,
         bool isReader,
         ErrorMode mode) const {
-    const std::string parentName = "_hidl_" + name + "_parent";
-
-    out << "size_t " << parentName << ";\n\n";
 
     const std::string parcelObjDeref =
         parcelObj + (parcelObjIsPointer ? "->" : ".");
 
-    if (isReader) {
-        out << "_hidl_err = "
-            << parcelObjDeref
-            << "readBuffer("
-            << "sizeof(*"
-            << name
-            << "), &"
-            << parentName
-            << ", "
-            << " reinterpret_cast<const void **>("
-            << "&" << name
-            << "));\n";
-
-        handleError(out, mode);
+    if(containsInterface()){
+        for (const auto& field : *mFields) {
+            field->type().emitReaderWriter(out, name + "." + field->name(),
+                                               parcelObj, parcelObjIsPointer, isReader, mode);
+        }
     } else {
-        out << "_hidl_err = "
-            << parcelObjDeref
-            << "writeBuffer(&"
-            << name
-            << ", sizeof("
-            << name
-            << "), &"
-            << parentName
-            << ");\n";
+        const std::string parentName = "_hidl_" + name + "_parent";
 
-        handleError(out, mode);
+        out << "size_t " << parentName << ";\n\n";
+
+        if (isReader) {
+            out << "_hidl_err = " << parcelObjDeref << "readBuffer("
+                << "sizeof(*" << name << "), &" << parentName << ", "
+                << " const_cast<const void**>(reinterpret_cast<void **>("
+                << "&" << name << ")));\n";
+            handleError(out, mode);
+        } else {
+            out << "_hidl_err = "
+                << parcelObjDeref
+                << "writeBuffer(&"
+                << name
+                << ", sizeof("
+                << name
+                << "), &"
+                << parentName
+                << ");\n";
+            handleError(out, mode);
+        }
+        if (mStyle != STYLE_STRUCT) {
+            return;
+        }
+        if (needsEmbeddedReadWrite()) {
+            emitReaderWriterEmbedded(out, 0 /* depth */, name, name, /* sanitizedName */
+                                     isReader /* nameIsPointer */, parcelObj, parcelObjIsPointer,
+                                     isReader, mode, parentName, "0 /* parentOffset */");
+        }
     }
-
-    if (mStyle != STYLE_STRUCT || !needsEmbeddedReadWrite()) {
-        return;
-    }
-
-    emitReaderWriterEmbedded(
-            out,
-            0 /* depth */,
-            name,
-            name, /* sanitizedName */
-            isReader /* nameIsPointer */,
-            parcelObj,
-            parcelObjIsPointer,
-            isReader,
-            mode,
-            parentName,
-            "0 /* parentOffset */");
 }
 
 void CompoundType::emitReaderWriterEmbedded(
@@ -347,7 +374,7 @@
     handleError(out, mode);
 }
 
-status_t CompoundType::emitTypeDeclarations(Formatter &out) const {
+void CompoundType::emitTypeDeclarations(Formatter& out) const {
     out << ((mStyle == STYLE_STRUCT) ? "struct" : "union")
         << " "
         << localName()
@@ -359,6 +386,7 @@
 
     if (containsPointer()) {
         for (const auto &field : *mFields) {
+            field->emitDocComment(out);
             out << field->type().getCppStackType()
                 << " "
                 << field->name()
@@ -368,7 +396,7 @@
         out.unindent();
         out << "};\n\n";
 
-        return OK;
+        return;
     }
 
     for (int pass = 0; pass < 2; ++pass) {
@@ -424,32 +452,62 @@
         << ") == "
         << structAlign
         << ", \"wrong alignment\");\n\n";
-
-    return OK;
 }
 
+void CompoundType::emitTypeForwardDeclaration(Formatter& out) const {
+    out << ((mStyle == STYLE_STRUCT) ? "struct" : "union") << " " << localName() << ";\n";
+}
 
-status_t CompoundType::emitGlobalTypeDeclarations(Formatter &out) const {
-    Scope::emitGlobalTypeDeclarations(out);
+void CompoundType::emitPackageTypeDeclarations(Formatter& out) const {
+    Scope::emitPackageTypeDeclarations(out);
 
-    out << "std::string toString("
+    out << "static inline std::string toString("
         << getCppArgumentType()
-        << ");\n\n";
+        << (mFields->empty() ? "" : " o")
+        << ") ";
+
+    out.block([&] {
+        // include toString for scalar types
+        out << "using ::android::hardware::toString;\n"
+            << "std::string os;\n";
+        out << "os += \"{\";\n";
+
+        for (const NamedReference<Type>* field : *mFields) {
+            out << "os += \"";
+            if (field != *(mFields->begin())) {
+                out << ", ";
+            }
+            out << "." << field->name() << " = \";\n";
+            field->type().emitDump(out, "os", "o." + field->name());
+        }
+
+        out << "os += \"}\"; return os;\n";
+    }).endl().endl();
 
     if (canCheckEquality()) {
-        out << "bool operator==("
-            << getCppArgumentType() << ", " << getCppArgumentType() << ");\n\n";
+        out << "static inline bool operator==("
+            << getCppArgumentType() << " " << (mFields->empty() ? "/* lhs */" : "lhs") << ", "
+            << getCppArgumentType() << " " << (mFields->empty() ? "/* rhs */" : "rhs") << ") ";
+        out.block([&] {
+            for (const auto &field : *mFields) {
+                out.sIf("lhs." + field->name() + " != rhs." + field->name(), [&] {
+                    out << "return false;\n";
+                }).endl();
+            }
+            out << "return true;\n";
+        }).endl().endl();
 
-        out << "bool operator!=("
-            << getCppArgumentType() << ", " << getCppArgumentType() << ");\n\n";
+        out << "static inline bool operator!=("
+            << getCppArgumentType() << " lhs," << getCppArgumentType() << " rhs)";
+        out.block([&] {
+            out << "return !(lhs == rhs);\n";
+        }).endl().endl();
     } else {
         out << "// operator== and operator!= are not generated for " << localName() << "\n\n";
     }
-
-    return OK;
 }
 
-status_t CompoundType::emitGlobalHwDeclarations(Formatter &out) const  {
+void CompoundType::emitPackageHwDeclarations(Formatter& out) const {
     if (needsEmbeddedReadWrite()) {
         out << "::android::status_t readEmbeddedFromParcel(\n";
 
@@ -488,18 +546,11 @@
             << "size_t parentHandle, size_t parentOffset);\n\n";
         out.unindent(2);
     }
-
-    return OK;
 }
 
-status_t CompoundType::emitTypeDefinitions(
-        Formatter &out, const std::string prefix) const {
+void CompoundType::emitTypeDefinitions(Formatter& out, const std::string& prefix) const {
     std::string space = prefix.empty() ? "" : (prefix + "::");
-    status_t err = Scope::emitTypeDefinitions(out, space + localName());
-
-    if (err != OK) {
-        return err;
-    }
+    Scope::emitTypeDefinitions(out, space + localName());
 
     if (needsEmbeddedReadWrite()) {
         emitStructReaderWriter(out, prefix, true /* isReader */);
@@ -510,57 +561,9 @@
         emitResolveReferenceDef(out, prefix, true /* isReader */);
         emitResolveReferenceDef(out, prefix, false /* isReader */);
     }
-
-    out << "std::string toString("
-        << getCppArgumentType()
-        << (mFields->empty() ? "" : " o")
-        << ") ";
-
-    out.block([&] {
-        // include toString for scalar types
-        out << "using ::android::hardware::toString;\n"
-            << "std::string os;\n";
-        out << "os += \"{\";\n";
-
-        for (const CompoundField *field : *mFields) {
-            out << "os += \"";
-            if (field != *(mFields->begin())) {
-                out << ", ";
-            }
-            out << "." << field->name() << " = \";\n";
-            field->type().emitDump(out, "os", "o." + field->name());
-        }
-
-        out << "os += \"}\"; return os;\n";
-    }).endl().endl();
-
-    if (canCheckEquality()) {
-        out << "bool operator==("
-            << getCppArgumentType() << " " << (mFields->empty() ? "/* lhs */" : "lhs") << ", "
-            << getCppArgumentType() << " " << (mFields->empty() ? "/* rhs */" : "rhs") << ") ";
-        out.block([&] {
-            for (const auto &field : *mFields) {
-                out.sIf("lhs." + field->name() + " != rhs." + field->name(), [&] {
-                    out << "return false;\n";
-                }).endl();
-            }
-            out << "return true;\n";
-        }).endl().endl();
-
-        out << "bool operator!=("
-            << getCppArgumentType() << " lhs," << getCppArgumentType() << " rhs)";
-        out.block([&] {
-            out << "return !(lhs == rhs);\n";
-        }).endl().endl();
-    } else {
-        out << "// operator== and operator!= are not generated for " << localName() << "\n";
-    }
-
-    return OK;
 }
 
-status_t CompoundType::emitJavaTypeDeclarations(
-        Formatter &out, bool atTopLevel) const {
+void CompoundType::emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const {
     out << "public final ";
 
     if (!atTopLevel) {
@@ -575,7 +578,9 @@
 
     Scope::emitJavaTypeDeclarations(out, false /* atTopLevel */);
 
-    for (const auto &field : *mFields) {
+    for (const auto& field : *mFields) {
+        field->emitDocComment(out);
+
         out << "public ";
 
         field->type().emitJavaFieldInitializer(out, field->name());
@@ -651,9 +656,16 @@
 
     out << "public final void readFromParcel(android.os.HwParcel parcel) {\n";
     out.indent();
-    out << "android.os.HwBlob blob = parcel.readBuffer(";
-    out << structSize << "/* size */);\n";
-    out << "readEmbeddedFromParcel(parcel, blob, 0 /* parentOffset */);\n";
+    if (containsInterface()) {
+        for (const auto& field : *mFields) {
+            out << field->name() << " = ";
+            field->type().emitJavaReaderWriter(out, "parcel", field->name(), true);
+        }
+    } else {
+        out << "android.os.HwBlob blob = parcel.readBuffer(";
+        out << structSize << "/* size */);\n";
+        out << "readEmbeddedFromParcel(parcel, blob, 0 /* parentOffset */);\n";
+    }
     out.unindent();
     out << "}\n\n";
 
@@ -662,77 +674,75 @@
     size_t vecAlign, vecSize;
     VectorType::getAlignmentAndSizeStatic(&vecAlign, &vecSize);
 
-    out << "public static final java.util.ArrayList<"
-        << localName()
+    out << "public static final java.util.ArrayList<" << localName()
         << "> readVectorFromParcel(android.os.HwParcel parcel) {\n";
     out.indent();
 
-    out << "java.util.ArrayList<"
-        << localName()
-        << "> _hidl_vec = new java.util.ArrayList();\n";
+    out << "java.util.ArrayList<" << localName() << "> _hidl_vec = new java.util.ArrayList();\n";
 
-    out << "android.os.HwBlob _hidl_blob = parcel.readBuffer(";
-    out << vecSize << " /* sizeof hidl_vec<T> */);\n\n";
+    if (containsInterface()) {
+        out << "int size = parcel.readInt32();\n";
+        out << "for(int i = 0 ; i < size; i ++) {\n";
+        out.indent();
+        out << fullJavaName() << " tmp = ";
+        emitJavaReaderWriter(out, "parcel", "tmp", true);
+        out << "_hidl_vec.add(tmp);\n";
+        out.unindent();
+        out << "}\n";
+    } else {
+        out << "android.os.HwBlob _hidl_blob = parcel.readBuffer(";
+        out << vecSize << " /* sizeof hidl_vec<T> */);\n\n";
 
-    VectorType::EmitJavaFieldReaderWriterForElementType(
-            out,
-            0 /* depth */,
-            this,
-            "parcel",
-            "_hidl_blob",
-            "_hidl_vec",
-            "0",
-            true /* isReader */);
-
-    out << "\nreturn _hidl_vec;\n";
-
-    out.unindent();
-    out << "}\n\n";
-
-    ////////////////////////////////////////////////////////////////////////////
-
-    out << "public final void readEmbeddedFromParcel(\n";
-    out.indent(2);
-    out << "android.os.HwParcel parcel, 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);
-
-        size_t pad = offset % fieldAlign;
-        if (pad > 0) {
-            offset += fieldAlign - pad;
-        }
-
-        field->type().emitJavaFieldReaderWriter(
-                out,
-                0 /* depth */,
-                "parcel",
-                "_hidl_blob",
-                field->name(),
-                "_hidl_offset + " + std::to_string(offset),
-                true /* isReader */);
-
-        offset += fieldSize;
+        VectorType::EmitJavaFieldReaderWriterForElementType(out, 0 /* depth */, this, "parcel",
+                                                            "_hidl_blob", "_hidl_vec", "0",
+                                                            true /* isReader */);
     }
-
+    out << "\nreturn _hidl_vec;\n";
     out.unindent();
     out << "}\n\n";
+    ////////////////////////////////////////////////////////////////////////////
+    if (containsInterface()) {
+        out << "// readEmbeddedFromParcel is not generated()\n";
+    } else {
+        out << "public final void readEmbeddedFromParcel(\n";
+        out.indent(2);
+        out << "android.os.HwParcel parcel, 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);
+
+            size_t pad = offset % fieldAlign;
+            if (pad > 0) {
+                offset += fieldAlign - pad;
+            }
+
+            field->type().emitJavaFieldReaderWriter(
+                out, 0 /* depth */, "parcel", "_hidl_blob", field->name(),
+                "_hidl_offset + " + std::to_string(offset), true /* isReader */);
+            offset += fieldSize;
+        }
+        out.unindent();
+        out << "}\n\n";
+    }
 
     ////////////////////////////////////////////////////////////////////////////
 
     out << "public final void writeToParcel(android.os.HwParcel parcel) {\n";
     out.indent();
 
-    out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob("
-        << structSize
-        << " /* size */);\n";
+    if (containsInterface()) {
+        for (const auto& field : *mFields) {
+            field->type().emitJavaReaderWriter(out, "parcel", field->name(), false);
+        }
+    } else {
+        out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob(" << structSize
+            << " /* size */);\n";
 
-    out << "writeEmbeddedToBlob(_hidl_blob, 0 /* parentOffset */);\n"
-        << "parcel.writeBuffer(_hidl_blob);\n";
-
+        out << "writeEmbeddedToBlob(_hidl_blob, 0 /* parentOffset */);\n"
+            << "parcel.writeBuffer(_hidl_blob);\n";
+    }
     out.unindent();
     out << "}\n\n";
 
@@ -740,65 +750,56 @@
 
     out << "public static final void writeVectorToParcel(\n";
     out.indent(2);
-    out << "android.os.HwParcel parcel, java.util.ArrayList<"
-        << localName()
-        << "> _hidl_vec) {\n";
+    out << "android.os.HwParcel parcel, java.util.ArrayList<" << localName() << "> _hidl_vec) {\n";
     out.unindent();
 
-    out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob("
-        << vecSize << " /* sizeof(hidl_vec<T>) */);\n";
+    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();
+    } else {
+        out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob(" << vecSize
+            << " /* sizeof(hidl_vec<T>) */);\n";
 
-    VectorType::EmitJavaFieldReaderWriterForElementType(
-            out,
-            0 /* depth */,
-            this,
-            "parcel",
-            "_hidl_blob",
-            "_hidl_vec",
-            "0",
-            false /* isReader */);
+        VectorType::EmitJavaFieldReaderWriterForElementType(out, 0 /* depth */, this, "parcel",
+                                                            "_hidl_blob", "_hidl_vec", "0",
+                                                            false /* isReader */);
 
-    out << "\nparcel.writeBuffer(_hidl_blob);\n";
-
+        out << "\nparcel.writeBuffer(_hidl_blob);\n";
+    }
     out.unindent();
     out << "}\n\n";
-
     ////////////////////////////////////////////////////////////////////////////
 
-    out << "public final void writeEmbeddedToBlob(\n";
-    out.indent(2);
-    out << "android.os.HwBlob _hidl_blob, long _hidl_offset) {\n";
-    out.unindent();
-
-    offset = 0;
-    for (const auto &field : *mFields) {
-        size_t fieldAlign, fieldSize;
-        field->type().getAlignmentAndSize(&fieldAlign, &fieldSize);
-
-        size_t pad = offset % fieldAlign;
-        if (pad > 0) {
-            offset += fieldAlign - pad;
+    if (containsInterface()) {
+        out << "// writeEmbeddedFromParcel() 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);
+            size_t pad = offset % fieldAlign;
+            if (pad > 0) {
+                offset += fieldAlign - pad;
+            }
+            field->type().emitJavaFieldReaderWriter(
+                out, 0 /* depth */, "parcel", "_hidl_blob", field->name(),
+                "_hidl_offset + " + std::to_string(offset), false /* isReader */);
+            offset += fieldSize;
         }
 
-        field->type().emitJavaFieldReaderWriter(
-                out,
-                0 /* depth */,
-                "parcel",
-                "_hidl_blob",
-                field->name(),
-                "_hidl_offset + " + std::to_string(offset),
-                false /* isReader */);
-
-        offset += fieldSize;
+        out.unindent();
+        out << "}\n";
     }
 
     out.unindent();
-    out << "}\n";
-
-    out.unindent();
     out << "};\n\n";
-
-    return OK;
 }
 
 void CompoundType::emitStructReaderWriter(
@@ -872,8 +873,8 @@
     out << "}\n\n";
 }
 
-void CompoundType::emitResolveReferenceDef(
-        Formatter &out, const std::string prefix, bool isReader) const {
+void CompoundType::emitResolveReferenceDef(Formatter& out, const std::string& prefix,
+                                           bool isReader) const {
     out << "::android::status_t ";
     const std::string space(prefix.empty() ? "" : (prefix + "::"));
 
@@ -962,25 +963,25 @@
     return false;
 }
 
-bool CompoundType::needsResolveReferences() const {
+bool CompoundType::deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const {
     if (mStyle != STYLE_STRUCT) {
         return false;
     }
 
     for (const auto &field : *mFields) {
-        if (field->type().needsResolveReferences()) {
+        if (field->type().needsResolveReferences(visited)) {
             return true;
         }
     }
 
-    return false;
+    return Scope::deepNeedsResolveReferences(visited);
 }
 
 bool CompoundType::resultNeedsDeref() const {
-    return true;
+    return !containsInterface() ;
 }
 
-status_t CompoundType::emitVtsTypeDeclarations(Formatter &out) const {
+void CompoundType::emitVtsTypeDeclarations(Formatter& out) const {
     out << "name: \"" << fullName() << "\"\n";
     out << "type: " << getVtsType() << "\n";
 
@@ -999,10 +1000,7 @@
             }
         }
         out.indent();
-        status_t status(type->emitVtsTypeDeclarations(out));
-        if (status != OK) {
-            return status;
-        }
+        type->emitVtsTypeDeclarations(out);
         out.unindent();
         out << "}\n";
     }
@@ -1023,49 +1021,39 @@
         }
         out.indent();
         out << "name: \"" << field->name() << "\"\n";
-        status_t status = field->type().emitVtsAttributeType(out);
-        if (status != OK) {
-            return status;
-        }
+        field->type().emitVtsAttributeType(out);
         out.unindent();
         out << "}\n";
     }
-
-    return OK;
 }
 
-status_t CompoundType::emitVtsAttributeType(Formatter &out) const {
+void CompoundType::emitVtsAttributeType(Formatter& out) const {
     out << "type: " << getVtsType() << "\n";
     out << "predefined_type: \"" << fullName() << "\"\n";
-    return OK;
 }
 
-bool CompoundType::isJavaCompatible() const {
-    if (mStyle != STYLE_STRUCT || !Scope::isJavaCompatible()) {
+bool CompoundType::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
+    if (mStyle != STYLE_STRUCT) {
         return false;
     }
 
-    for (const auto &field : *mFields) {
-        if (!field->type().isJavaCompatible()) {
+    for (const auto* field : *mFields) {
+        if (!field->get()->isJavaCompatible(visited)) {
             return false;
         }
     }
 
-    return true;
+    return Scope::deepIsJavaCompatible(visited);
 }
 
-bool CompoundType::containsPointer() const {
-    if (Scope::containsPointer()) {
-        return true;
-    }
-
-    for (const auto &field : *mFields) {
-        if (field->type().containsPointer()) {
+bool CompoundType::deepContainsPointer(std::unordered_set<const Type*>* visited) const {
+    for (const auto* field : *mFields) {
+        if (field->get()->containsPointer(visited)) {
             return true;
         }
     }
 
-    return false;
+    return Scope::deepContainsPointer(visited);
 }
 
 void CompoundType::getAlignmentAndSize(size_t *align, size_t *size) const {
@@ -1113,20 +1101,5 @@
     }
 }
 
-////////////////////////////////////////////////////////////////////////////////
-
-CompoundField::CompoundField(const char *name, Type *type)
-    : mName(name),
-      mType(type) {
-}
-
-std::string CompoundField::name() const {
-    return mName;
-}
-
-const Type &CompoundField::type() const {
-    return *mType;
-}
-
 }  // namespace android
 
diff --git a/CompoundType.h b/CompoundType.h
index 4917dec..3ef5f02 100644
--- a/CompoundType.h
+++ b/CompoundType.h
@@ -18,32 +18,37 @@
 
 #define COMPOUND_TYPE_H_
 
+#include "Reference.h"
 #include "Scope.h"
 
 #include <vector>
 
 namespace android {
 
-struct CompoundField;
-
 struct CompoundType : public Scope {
     enum Style {
         STYLE_STRUCT,
         STYLE_UNION,
     };
 
-    CompoundType(Style style, const char* localName, const Location& location, Scope* parent);
+    CompoundType(Style style, const char* localName, const FQName& fullName,
+                 const Location& location, Scope* parent);
 
     Style style() const;
 
-    bool setFields(std::vector<CompoundField *> *fields, std::string *errorMsg);
+    void setFields(std::vector<NamedReference<Type>*>* fields);
 
     bool isCompoundType() const override;
 
-    bool canCheckEquality() const override;
+    bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
 
     std::string typeName() const override;
 
+    std::vector<const Reference<Type>*> getReferences() const override;
+
+    status_t validate() const override;
+    status_t validateUniqueNames() const;
+
     std::string getCppType(StorageMode mode,
                            bool specifyNamespaces) const override;
 
@@ -112,53 +117,39 @@
             const std::string &offset,
             bool isReader) const override;
 
-    status_t emitTypeDeclarations(Formatter &out) const override;
-    status_t emitGlobalTypeDeclarations(Formatter &out) const override;
-    status_t emitGlobalHwDeclarations(Formatter &out) const override;
+    void emitTypeDeclarations(Formatter& out) const override;
+    void emitTypeForwardDeclaration(Formatter& out) const override;
+    void emitPackageTypeDeclarations(Formatter& out) const override;
+    void emitPackageHwDeclarations(Formatter& out) const override;
 
-    status_t emitTypeDefinitions(
-            Formatter &out, const std::string prefix) const override;
+    void emitTypeDefinitions(Formatter& out, const std::string& prefix) const override;
 
-    status_t emitJavaTypeDeclarations(
-            Formatter &out, bool atTopLevel) const override;
+    void emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const override;
 
     bool needsEmbeddedReadWrite() const override;
-    bool needsResolveReferences() const override;
+    bool deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const override;
     bool resultNeedsDeref() const override;
 
-    status_t emitVtsTypeDeclarations(Formatter &out) const override;
-    status_t emitVtsAttributeType(Formatter &out) const override;
+    void emitVtsTypeDeclarations(Formatter& out) const override;
+    void emitVtsAttributeType(Formatter& out) const override;
 
-    bool isJavaCompatible() const override;
-    bool containsPointer() const override;
+    bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
+    bool deepContainsPointer(std::unordered_set<const Type*>* visited) const override;
 
     void getAlignmentAndSize(size_t *align, size_t *size) const;
 
+    bool containsInterface() const;
 private:
     Style mStyle;
-    std::vector<CompoundField *> *mFields;
+    std::vector<NamedReference<Type>*>* mFields;
 
     void emitStructReaderWriter(
             Formatter &out, const std::string &prefix, bool isReader) const;
-    void emitResolveReferenceDef(
-        Formatter &out, const std::string prefix, bool isReader) const;
+    void emitResolveReferenceDef(Formatter& out, const std::string& prefix, bool isReader) const;
 
     DISALLOW_COPY_AND_ASSIGN(CompoundType);
 };
 
-struct CompoundField {
-    CompoundField(const char *name, Type *type);
-
-    std::string name() const;
-    const Type &type() const;
-
-private:
-    std::string mName;
-    Type *mType;
-
-    DISALLOW_COPY_AND_ASSIGN(CompoundField);
-};
-
 }  // namespace android
 
 #endif  // COMPOUND_TYPE_H_
diff --git a/ConstantExpression.cpp b/ConstantExpression.cpp
index eba28e1..8a2b875 100644
--- a/ConstantExpression.cpp
+++ b/ConstantExpression.cpp
@@ -16,21 +16,23 @@
 
 #include "ConstantExpression.h"
 
-#include <stdio.h>
-#include <string>
-#include <android-base/parseint.h>
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <stdio.h>
+#include <algorithm>
+#include <iostream>
 #include <sstream>
+#include <string>
 
 #include "EnumType.h"
+#include "Scope.h"  // LocalIdentifier
 
 // The macros are really nasty here. Consider removing
 // as many macros as possible.
 
-#define STREQ(__x__, __y__) (strcmp((__x__), (__y__)) == 0)
-#define OPEQ(__y__) STREQ(op, __y__)
-#define COMPUTE_UNARY(__op__)  if(OPEQ(#__op__)) return __op__ val;
-#define COMPUTE_BINARY(__op__) if(OPEQ(#__op__)) return lval __op__ rval;
+#define OPEQ(__y__) (std::string(mOp) == std::string(__y__))
+#define COMPUTE_UNARY(__op__)  if (op == std::string(#__op__)) return __op__ val;
+#define COMPUTE_BINARY(__op__) if (op == std::string(#__op__)) return lval __op__ rval;
 #define OP_IS_BIN_ARITHMETIC  (OPEQ("+") || OPEQ("-") || OPEQ("*") || OPEQ("/") || OPEQ("%"))
 #define OP_IS_BIN_BITFLIP     (OPEQ("|") || OPEQ("^") || OPEQ("&"))
 #define OP_IS_BIN_COMP        (OPEQ("<") || OPEQ(">") || OPEQ("<=") || OPEQ(">=") || OPEQ("==") || OPEQ("!="))
@@ -39,6 +41,7 @@
 #define SK(__x__) ScalarType::Kind::KIND_##__x__
 #define SHOULD_NOT_REACH() CHECK(false) << __LINE__ << ": should not reach here: "
 
+// NOLINT to suppress missing parentheses warnings about __def__.
 #define SWITCH_KIND(__cond__, __action__, __def__)           \
         switch(__cond__) {                                        \
             case SK(BOOL): __action__(bool)                         \
@@ -50,13 +53,13 @@
             case SK(INT32): __action__(int32_t)                     \
             case SK(UINT64): __action__(uint64_t)                   \
             case SK(INT64): __action__(int64_t)                     \
-            default: __def__                                        \
-        }                                                         \
+            default: __def__                        /* NOLINT */    \
+        }
 
 namespace android {
 
 static inline bool isSupported(ScalarType::Kind kind) {
-    return SK(BOOL) == kind || ScalarType(kind).isValidEnumStorageType();
+    return SK(BOOL) == kind || ScalarType(kind, nullptr /* parent */).isValidEnumStorageType();
 }
 
 /* See docs at the end for details on integral promotion. */
@@ -84,8 +87,7 @@
 
     // Although there is such rule to return "the unsigned counterpart of
     // the signed operand", it should not reach here in our HIDL grammar.
-    LOG(FATAL) << "Could not do usual arithmetic conversion for type "
-               << lft << "and" << rgt;
+    CHECK(false) << "Could not do usual arithmetic conversion for type " << lft << "and" << rgt;
     switch(signedRank) {
         case SK(INT8):  return SK(UINT8);
         case SK(INT16): return SK(UINT16);
@@ -96,7 +98,7 @@
 }
 
 template <class T>
-T handleUnary(const char *op, T val) {
+T handleUnary(const std::string& op, T val) {
     COMPUTE_UNARY(+)
     COMPUTE_UNARY(-)
     COMPUTE_UNARY(!)
@@ -107,7 +109,7 @@
 }
 
 template <class T>
-T handleBinaryCommon(T lval, const char *op, T rval) {
+T handleBinaryCommon(T lval, const std::string& op, T rval) {
     COMPUTE_BINARY(+)
     COMPUTE_BINARY(-)
     COMPUTE_BINARY(*)
@@ -130,173 +132,182 @@
 }
 
 template <class T>
-T handleShift(T lval, const char *op, int64_t rval) {
+T handleShift(T lval, const std::string& op, int64_t rval) {
     // just cast rval to int64_t and it should fit.
     COMPUTE_BINARY(>>)
     COMPUTE_BINARY(<<)
     // Should not reach here.
-    SHOULD_NOT_REACH() << "Could not handleShift for"
+    SHOULD_NOT_REACH() << "Could not handleShift for "
                        << lval << " " << op << " " << rval;
     return static_cast<T>(0xdeadbeef);
 }
 
-bool handleLogical(bool lval, const char *op, bool rval) {
+bool handleLogical(bool lval, const std::string& op, bool rval) {
     COMPUTE_BINARY(||);
     COMPUTE_BINARY(&&);
     // Should not reach here.
-    SHOULD_NOT_REACH() << "Could not handleLogical for"
+    SHOULD_NOT_REACH() << "Could not handleLogical for "
                        << lval << " " << op << " " << rval;
     return false;
 }
 
-ConstantExpression::ConstantExpression() {
+std::unique_ptr<ConstantExpression> ConstantExpression::Zero(ScalarType::Kind kind) {
+    return ValueOf(kind, 0);
 }
 
-ConstantExpression ConstantExpression::Zero(ScalarType::Kind kind) {
-    ConstantExpression ce = ValueOf(kind, 0);
-    ce.mExpr = "0";
-    return ce;
+std::unique_ptr<ConstantExpression> ConstantExpression::One(ScalarType::Kind kind) {
+    return ValueOf(kind, 1);
 }
 
-ConstantExpression ConstantExpression::One(ScalarType::Kind kind) {
-    ConstantExpression ce = ValueOf(kind, 1);
-    ce.mExpr = "1";
-    return ce;
+std::unique_ptr<ConstantExpression> ConstantExpression::ValueOf(ScalarType::Kind kind,
+                                                                uint64_t value) {
+    return std::make_unique<LiteralConstantExpression>(kind, value);
 }
 
-ConstantExpression ConstantExpression::ValueOf(ScalarType::Kind kind, uint64_t value) {
-    ConstantExpression ce;
+bool ConstantExpression::isEvaluated() const {
+    return mIsEvaluated;
+}
+
+LiteralConstantExpression::LiteralConstantExpression(
+    ScalarType::Kind kind, uint64_t value, const std::string& expr) {
+
+    CHECK(!expr.empty());
     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;
-}
-
-/* Copy constructor, with the expr overriden and treated non-trivial */
-ConstantExpression::ConstantExpression(const ConstantExpression& other, std::string expr) {
-    *this = other;
+    mTrivialDescription = true;
     mExpr = expr;
-    mTrivialDescription = false;
+    mValueKind = kind;
+    mValue = value;
+    mIsEvaluated = true;
 }
 
-ConstantExpression& ConstantExpression::operator=(const ConstantExpression& other) {
-    mType = other.mType;
-    mValueKind = other.mValueKind;
-    mValue = other.mValue;
-    mExpr = other.mExpr;
-    mTrivialDescription = other.mTrivialDescription;
-    return *this;
-}
+LiteralConstantExpression::LiteralConstantExpression(ScalarType::Kind kind, uint64_t value)
+  : LiteralConstantExpression(kind, value, std::to_string(value)) {}
 
-/* Literals. */
-ConstantExpression::ConstantExpression(const char *value)
-        : mExpr(value), mType(kConstExprLiteral), mTrivialDescription(true) {
-    const char* head = value, *tail = head + strlen(value) - 1;
+LiteralConstantExpression* LiteralConstantExpression::tryParse(const std::string& value) {
+    CHECK(!value.empty());
+
     bool isLong = false, isUnsigned = false;
-    bool isHex = (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'));
-    while(tail >= head && (*tail == 'u' || *tail == 'U' || *tail == 'l' || *tail == 'L')) {
-        isUnsigned |= (*tail == 'u' || *tail == 'U');
-        isLong     |= (*tail == 'l' || *tail == 'L');
-        tail--;
+    bool isHex = (value[0] == '0' && value.length() > 1 && (value[1] == 'x' || value[1] == 'X'));
+
+    auto rbegin = value.rbegin();
+    auto rend = value.rend();
+    for (; rbegin != rend && (*rbegin == 'u' || *rbegin == 'U' || *rbegin == 'l' || *rbegin == 'L');
+         ++rbegin) {
+        isUnsigned |= (*rbegin == 'u' || *rbegin == 'U');
+        isLong |= (*rbegin == 'l' || *rbegin == 'L');
     }
-    char *newVal = strndup(value, tail - head + 1);
-    bool parseOK = base::ParseUint(newVal, &mValue);
-    free(newVal);
-    CHECK(parseOK) << "Could not parse as integer: " << value;
+    std::string newVal(value.begin(), rbegin.base());
+    CHECK(!newVal.empty());
+
+    uint64_t rawValue = 0;
+
+    bool parseOK = base::ParseUint(newVal, &rawValue);
+    if (!parseOK) {
+        return nullptr;
+    }
+
+    ScalarType::Kind kind;
 
     // guess literal type.
     if(isLong) {
         if(isUnsigned) // ul
-            mValueKind = SK(UINT64);
+            kind = SK(UINT64);
         else // l
-            mValueKind = SK(INT64);
+            kind = SK(INT64);
     } else { // no l suffix
         if(isUnsigned) { // u
-            if(mValue <= UINT32_MAX)
-                mValueKind = SK(UINT32);
+            if(rawValue <= UINT32_MAX)
+                kind = SK(UINT32);
             else
-                mValueKind = SK(UINT64);
+                kind = SK(UINT64);
         } else { // no suffix
             if(isHex) {
-                if(mValue <= INT32_MAX) // mValue always >= 0
-                    mValueKind = SK(INT32);
-                else if(mValue <= UINT32_MAX)
-                    mValueKind = SK(UINT32);
-                else if(mValue <= INT64_MAX) // mValue always >= 0
-                    mValueKind = SK(INT64);
-                else if(mValue <= UINT64_MAX)
-                    mValueKind = SK(UINT64);
-            } else {
-                if(mValue <= INT32_MAX) // mValue always >= 0
-                    mValueKind = SK(INT32);
+                if(rawValue <= INT32_MAX) // rawValue always >= 0
+                    kind = SK(INT32);
+                else if(rawValue <= UINT32_MAX)
+                    kind = SK(UINT32);
+                else if(rawValue <= INT64_MAX) // rawValue always >= 0
+                    kind = SK(INT64);
+                else if(rawValue <= UINT64_MAX)
+                    kind = SK(UINT64);
                 else
-                    mValueKind = SK(INT64);
+                    return nullptr;
+            } else {
+                if(rawValue <= INT32_MAX) // rawValue always >= 0
+                    kind = SK(INT32);
+                else
+                    kind = SK(INT64);
             }
         }
     }
+
+    return new LiteralConstantExpression(kind, rawValue, value);
 }
 
-/* Unary operations. */
-ConstantExpression::ConstantExpression(const char *op,
-                                       const ConstantExpression *value)
-        : mExpr(std::string("(") + op + value->mExpr + ")"),
-          mType(kConstExprUnary),
-          mValueKind(value->mValueKind) {
+void LiteralConstantExpression::evaluate() {
+    // Evaluated in constructor
+    CHECK(isEvaluated());
+}
 
-#define CASE_UNARY(__type__)\
-            mValue = handleUnary(op, static_cast<__type__>(value->mValue)); return;
+void UnaryConstantExpression::evaluate() {
+    if (isEvaluated()) return;
+    CHECK(mUnary->isEvaluated());
+    mIsEvaluated = true;
+
+    mExpr = std::string("(") + mOp + mUnary->description() + ")";
+    mValueKind = mUnary->mValueKind;
+
+#define CASE_UNARY(__type__)                                          \
+    mValue = handleUnary(mOp, static_cast<__type__>(mUnary->mValue)); \
+    return;
 
     SWITCH_KIND(mValueKind, CASE_UNARY, SHOULD_NOT_REACH(); return;)
 }
 
-/* Binary operations. */
-ConstantExpression::ConstantExpression(const ConstantExpression *lval,
-                                       const char *op,
-                                       const ConstantExpression* rval)
-        : mExpr(std::string("(") + lval->mExpr + " " + op + " " + rval->mExpr + ")"),
-          mType(kConstExprBinary)
-{
+void BinaryConstantExpression::evaluate() {
+    if (isEvaluated()) return;
+    CHECK(mLval->isEvaluated());
+    CHECK(mRval->isEvaluated());
+    mIsEvaluated = true;
+
+    mExpr = std::string("(") + mLval->description() + " " + mOp + " " + mRval->description() + ")";
 
     bool isArithmeticOrBitflip = OP_IS_BIN_ARITHMETIC || OP_IS_BIN_BITFLIP;
 
     // CASE 1: + - *  / % | ^ & < > <= >= == !=
     if(isArithmeticOrBitflip || OP_IS_BIN_COMP) {
         // promoted kind for both operands.
-        ScalarType::Kind promoted = usualArithmeticConversion(
-                integralPromotion(lval->mValueKind),
-                integralPromotion(rval->mValueKind));
+        ScalarType::Kind promoted = usualArithmeticConversion(integralPromotion(mLval->mValueKind),
+                                                              integralPromotion(mRval->mValueKind));
         // result kind.
         mValueKind = isArithmeticOrBitflip
                     ? promoted // arithmetic or bitflip operators generates promoted type
                     : SK(BOOL); // comparison operators generates bool
 
-#define CASE_BINARY_COMMON(__type__)\
-            mValue = handleBinaryCommon(static_cast<__type__>(lval->mValue), op, static_cast<__type__>(rval->mValue)); return;
+#define CASE_BINARY_COMMON(__type__)                                       \
+    mValue = handleBinaryCommon(static_cast<__type__>(mLval->mValue), mOp, \
+                                static_cast<__type__>(mRval->mValue));     \
+    return;
 
         SWITCH_KIND(promoted, CASE_BINARY_COMMON, SHOULD_NOT_REACH(); return;)
     }
 
     // CASE 2: << >>
+    std::string newOp = mOp;
     if(OP_IS_BIN_SHIFT) {
-        mValueKind = integralPromotion(lval->mValueKind);
+        mValueKind = integralPromotion(mLval->mValueKind);
         // instead of promoting rval, simply casting it to int64 should also be good.
-        int64_t numBits = rval->cast<int64_t>();
+        int64_t numBits = mRval->cast<int64_t>();
         if(numBits < 0) {
             // shifting with negative number of bits is undefined in C. In HIDL it
             // is defined as shifting into the other direction.
-            op = OPEQ("<<") ? ">>" : "<<";
+            newOp = OPEQ("<<") ? std::string(">>") : std::string("<<");
             numBits = -numBits;
         }
 
-#define CASE_SHIFT(__type__)\
-            mValue = handleShift(static_cast<__type__>(lval->mValue), op, numBits); return;
+#define CASE_SHIFT(__type__)                                                    \
+    mValue = handleShift(static_cast<__type__>(mLval->mValue), newOp, numBits); \
+    return;
 
         SWITCH_KIND(mValueKind, CASE_SHIFT, SHOULD_NOT_REACH(); return;)
     }
@@ -305,63 +316,79 @@
     if(OP_IS_BIN_LOGICAL) {
         mValueKind = SK(BOOL);
         // easy; everything is bool.
-        mValue = handleLogical(lval->mValue, op, rval->mValue);
+        mValue = handleLogical(mLval->mValue, mOp, mRval->mValue);
         return;
     }
 
     SHOULD_NOT_REACH();
 }
 
-/* Ternary ?: operation. */
-ConstantExpression::ConstantExpression(const ConstantExpression *cond,
-                                       const ConstantExpression *trueVal,
-                                       const ConstantExpression *falseVal)
-        : mExpr(std::string("(") + cond->mExpr + "?" + trueVal->mExpr
-                + ":" + falseVal->mExpr + ")"),
-          mType(kConstExprTernary) {
+void TernaryConstantExpression::evaluate() {
+    if (isEvaluated()) return;
+    CHECK(mCond->isEvaluated());
+    CHECK(mTrueVal->isEvaluated());
+    CHECK(mFalseVal->isEvaluated());
+    mIsEvaluated = true;
 
-    // note: for ?:, unlike arithmetic ops, integral promotion is not necessary.
-    mValueKind = usualArithmeticConversion(trueVal->mValueKind,
-                                           falseVal->mValueKind);
+    mExpr = std::string("(") + mCond->description() + "?" + mTrueVal->description() + ":" +
+            mFalseVal->description() + ")";
 
-#define CASE_TERNARY(__type__)\
-        mValue = cond->mValue ? (static_cast<__type__>(trueVal->mValue)) : (static_cast<__type__>(falseVal->mValue)); return;
+    // note: for ?:, unlike arithmetic ops, integral promotion is not processed.
+    mValueKind = usualArithmeticConversion(mTrueVal->mValueKind, mFalseVal->mValueKind);
+
+#define CASE_TERNARY(__type__)                                           \
+    mValue = mCond->mValue ? (static_cast<__type__>(mTrueVal->mValue))   \
+                           : (static_cast<__type__>(mFalseVal->mValue)); \
+    return;
 
     SWITCH_KIND(mValueKind, CASE_TERNARY, SHOULD_NOT_REACH(); return;)
 }
 
-ConstantExpression ConstantExpression::addOne() const {
-    ConstantExpression myOne = ConstantExpression::One(mValueKind);
-    return ConstantExpression(this, "+", &myOne).toLiteral();
+void ReferenceConstantExpression::evaluate() {
+    if (isEvaluated()) return;
+    CHECK(mReference->constExpr() != nullptr);
+
+    ConstantExpression* expr = mReference->constExpr();
+    CHECK(expr->isEvaluated());
+
+    mValueKind = expr->mValueKind;
+    mValue = expr->mValue;
+    mIsEvaluated = true;
 }
 
-ConstantExpression &ConstantExpression::toLiteral() {
-    mExpr = value();
-    mType = kConstExprLiteral;
-    return *this;
+std::unique_ptr<ConstantExpression> ConstantExpression::addOne(ScalarType::Kind baseKind) {
+    auto ret = std::make_unique<BinaryConstantExpression>(
+        this, "+", ConstantExpression::One(baseKind).release());
+    return ret;
 }
 
-const std::string &ConstantExpression::description() const {
+const std::string& ConstantExpression::description() const {
+    CHECK(isEvaluated());
     return mExpr;
 }
 
 bool ConstantExpression::descriptionIsTrivial() const {
+    CHECK(isEvaluated());
     return mTrivialDescription;
 }
 
 std::string ConstantExpression::value() const {
+    CHECK(isEvaluated());
     return rawValue(mValueKind);
 }
 
 std::string ConstantExpression::value(ScalarType::Kind castKind) const {
+    CHECK(isEvaluated());
     return rawValue(castKind);
 }
 
 std::string ConstantExpression::cppValue() const {
+    CHECK(isEvaluated());
     return cppValue(mValueKind);
 }
 
 std::string ConstantExpression::cppValue(ScalarType::Kind castKind) const {
+    CHECK(isEvaluated());
     std::string literal(rawValue(castKind));
     // this is a hack to translate
     //       enum x : int64_t {  y = 1l << 63 };
@@ -372,9 +399,9 @@
     // -(uint64_t)9223372036854775808 == 9223372036854775808 could not
     // be narrowed to int64_t.
     if(castKind == SK(INT64) && (int64_t)mValue == INT64_MIN) {
-        return strdup(("static_cast<"
-            + ScalarType(SK(INT64)).getCppStackType() // "int64_t"
-            + ">(" + literal + "ull)").c_str());
+        return "static_cast<" +
+               ScalarType(SK(INT64), nullptr /* parent */).getCppStackType()  // "int64_t"
+               + ">(" + literal + "ull)";
     }
 
     // add suffix if necessary.
@@ -384,10 +411,12 @@
 }
 
 std::string ConstantExpression::javaValue() const {
+    CHECK(isEvaluated());
     return javaValue(mValueKind);
 }
 
 std::string ConstantExpression::javaValue(ScalarType::Kind castKind) const {
+    CHECK(isEvaluated());
     switch(castKind) {
         case SK(UINT64): return rawValue(SK(INT64)) + "L";
         case SK(INT64):  return rawValue(SK(INT64)) + "L";
@@ -395,13 +424,14 @@
         case SK(UINT16): return rawValue(SK(INT16));
         case SK(UINT8) : return rawValue(SK(INT8));
         case SK(BOOL)  :
-            return this->cast<bool>() ? strdup("true") : strdup("false");
+            return this->cast<bool>() ? "true" : "false";
         default: break;
     }
     return rawValue(castKind);
 }
 
 std::string ConstantExpression::rawValue(ScalarType::Kind castKind) const {
+    CHECK(isEvaluated());
 
 #define CASE_STR(__type__) return std::to_string(this->cast<__type__>());
 
@@ -410,6 +440,7 @@
 
 template<typename T>
 T ConstantExpression::cast() const {
+    CHECK(isEvaluated());
 
 #define CASE_CAST_T(__type__) return static_cast<T>(static_cast<__type__>(mValue));
 
@@ -417,9 +448,213 @@
 }
 
 size_t ConstantExpression::castSizeT() const {
+    CHECK(isEvaluated());
     return this->cast<size_t>();
 }
 
+bool ConstantExpression::isReferenceConstantExpression() const {
+    return false;
+}
+
+std::vector<ConstantExpression*> ConstantExpression::getConstantExpressions() {
+    const auto& constRet = static_cast<const ConstantExpression*>(this)->getConstantExpressions();
+    std::vector<ConstantExpression*> ret(constRet.size());
+    std::transform(constRet.begin(), constRet.end(), ret.begin(),
+                   [](const auto* ce) { return const_cast<ConstantExpression*>(ce); });
+    return ret;
+}
+
+std::vector<Reference<LocalIdentifier>*> ConstantExpression::getReferences() {
+    const auto& constRet = static_cast<const ConstantExpression*>(this)->getReferences();
+    std::vector<Reference<LocalIdentifier>*> ret(constRet.size());
+    std::transform(constRet.begin(), constRet.end(), ret.begin(),
+                   [](const auto* ce) { return const_cast<Reference<LocalIdentifier>*>(ce); });
+    return ret;
+}
+
+std::vector<const Reference<LocalIdentifier>*> ConstantExpression::getReferences() const {
+    return {};
+}
+
+status_t ConstantExpression::recursivePass(const std::function<status_t(ConstantExpression*)>& func,
+                                           std::unordered_set<const ConstantExpression*>* visited,
+                                           bool processBeforeDependencies) {
+    if (mIsPostParseCompleted) return OK;
+
+    if (visited->find(this) != visited->end()) return OK;
+    visited->insert(this);
+
+    if (processBeforeDependencies) {
+        status_t err = func(this);
+        if (err != OK) return err;
+    }
+
+    for (auto* nextCE : getConstantExpressions()) {
+        status_t err = nextCE->recursivePass(func, visited, processBeforeDependencies);
+        if (err != OK) return err;
+    }
+
+    for (auto* nextRef : getReferences()) {
+        auto* nextCE = nextRef->shallowGet()->constExpr();
+        CHECK(nextCE != nullptr) << "Local identifier is not a constant expression";
+        status_t err = nextCE->recursivePass(func, visited, processBeforeDependencies);
+        if (err != OK) return err;
+    }
+
+    if (!processBeforeDependencies) {
+        status_t err = func(this);
+        if (err != OK) return err;
+    }
+
+    return OK;
+}
+
+status_t ConstantExpression::recursivePass(
+    const std::function<status_t(const ConstantExpression*)>& func,
+    std::unordered_set<const ConstantExpression*>* visited, bool processBeforeDependencies) const {
+    if (mIsPostParseCompleted) return OK;
+
+    if (visited->find(this) != visited->end()) return OK;
+    visited->insert(this);
+
+    if (processBeforeDependencies) {
+        status_t err = func(this);
+        if (err != OK) return err;
+    }
+
+    for (const auto* nextCE : getConstantExpressions()) {
+        status_t err = nextCE->recursivePass(func, visited, processBeforeDependencies);
+        if (err != OK) return err;
+    }
+
+    for (const auto* nextRef : getReferences()) {
+        const auto* nextCE = nextRef->shallowGet()->constExpr();
+        CHECK(nextCE != nullptr) << "Local identifier is not a constant expression";
+        status_t err = nextCE->recursivePass(func, visited, processBeforeDependencies);
+        if (err != OK) return err;
+    }
+
+    if (!processBeforeDependencies) {
+        status_t err = func(this);
+        if (err != OK) return err;
+    }
+
+    return OK;
+}
+
+ConstantExpression::CheckAcyclicStatus::CheckAcyclicStatus(
+    status_t status, const ConstantExpression* cycleEnd,
+    const ReferenceConstantExpression* lastReference)
+    : status(status), cycleEnd(cycleEnd), lastReference(lastReference) {
+    CHECK(cycleEnd == nullptr || status != OK);
+    CHECK((cycleEnd == nullptr) == (lastReference == nullptr));
+}
+
+ConstantExpression::CheckAcyclicStatus ConstantExpression::checkAcyclic(
+    std::unordered_set<const ConstantExpression*>* visited,
+    std::unordered_set<const ConstantExpression*>* stack) const {
+    if (stack->find(this) != stack->end()) {
+        CHECK(isReferenceConstantExpression())
+            << "Only reference constant expression could be the cycle end";
+
+        std::cerr << "ERROR: Cyclic declaration:\n";
+        return CheckAcyclicStatus(UNKNOWN_ERROR, this,
+                                  static_cast<const ReferenceConstantExpression*>(this));
+    }
+
+    if (visited->find(this) != visited->end()) return CheckAcyclicStatus(OK);
+    visited->insert(this);
+    stack->insert(this);
+
+    for (const auto* nextCE : getConstantExpressions()) {
+        auto err = nextCE->checkAcyclic(visited, stack);
+        if (err.status != OK) {
+            return err;
+        }
+    }
+
+    for (const auto* nextRef : getReferences()) {
+        const auto* nextCE = nextRef->shallowGet()->constExpr();
+        CHECK(nextCE != nullptr) << "Local identifier is not a constant expression";
+        auto err = nextCE->checkAcyclic(visited, stack);
+
+        if (err.status != OK) {
+            if (err.cycleEnd == nullptr) return err;
+
+            // Only ReferenceConstantExpression has references,
+            CHECK(isReferenceConstantExpression())
+                << "Only reference constant expression could have refereneces";
+
+            // mExpr is defined explicitly before evaluation
+            std::cerr << "  '" << err.lastReference->mExpr << "' in '" << mExpr << "' at "
+                      << nextRef->location() << "\n";
+
+            if (err.cycleEnd == this) {
+                return CheckAcyclicStatus(err.status);
+            }
+            return CheckAcyclicStatus(err.status, err.cycleEnd,
+                                      static_cast<const ReferenceConstantExpression*>(this));
+        }
+    }
+
+    CHECK(stack->find(this) != stack->end());
+    stack->erase(this);
+    return CheckAcyclicStatus(OK);
+}
+
+void ConstantExpression::setPostParseCompleted() {
+    CHECK(!mIsPostParseCompleted);
+    mIsPostParseCompleted = true;
+}
+
+std::vector<const ConstantExpression*> LiteralConstantExpression::getConstantExpressions() const {
+    return {};
+}
+
+UnaryConstantExpression::UnaryConstantExpression(const std::string& op, ConstantExpression* value)
+    : mUnary(value), mOp(op) {}
+
+std::vector<const ConstantExpression*> UnaryConstantExpression::getConstantExpressions() const {
+    return {mUnary};
+}
+
+BinaryConstantExpression::BinaryConstantExpression(ConstantExpression* lval, const std::string& op,
+                                                   ConstantExpression* rval)
+    : mLval(lval), mRval(rval), mOp(op) {}
+
+std::vector<const ConstantExpression*> BinaryConstantExpression::getConstantExpressions() const {
+    return {mLval, mRval};
+}
+
+TernaryConstantExpression::TernaryConstantExpression(ConstantExpression* cond,
+                                                     ConstantExpression* trueVal,
+                                                     ConstantExpression* falseVal)
+    : mCond(cond), mTrueVal(trueVal), mFalseVal(falseVal) {}
+
+std::vector<const ConstantExpression*> TernaryConstantExpression::getConstantExpressions() const {
+    return {mCond, mTrueVal, mFalseVal};
+}
+
+ReferenceConstantExpression::ReferenceConstantExpression(const Reference<LocalIdentifier>& value,
+                                                         const std::string& expr)
+    : mReference(value) {
+    mExpr = expr;
+    mTrivialDescription = mExpr.empty();
+}
+
+bool ReferenceConstantExpression::isReferenceConstantExpression() const {
+    return true;
+}
+
+std::vector<const ConstantExpression*> ReferenceConstantExpression::getConstantExpressions() const {
+    // Returns reference instead
+    return {};
+}
+
+std::vector<const Reference<LocalIdentifier>*> ReferenceConstantExpression::getReferences() const {
+    return {&mReference};
+}
+
 /*
 
 Evaluating expressions in HIDL language
diff --git a/ConstantExpression.h b/ConstantExpression.h
index 2c955d4..9784885 100644
--- a/ConstantExpression.h
+++ b/ConstantExpression.h
@@ -19,45 +19,80 @@
 #define CONSTANT_EXPRESSION_H_
 
 #include <android-base/macros.h>
+#include <functional>
+#include <memory>
 #include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "Reference.h"
 #include "ScalarType.h"
 
 namespace android {
 
+struct LocalIdentifier;
+
+struct LiteralConstantExpression;
+struct UnaryConstantExpression;
+struct BinaryConstantExpression;
+struct TernaryConstantExpression;
+struct ReferenceConstantExpression;
+
 /**
  * A constant expression is represented by a tree.
  */
 struct ConstantExpression {
+    static std::unique_ptr<ConstantExpression> Zero(ScalarType::Kind kind);
+    static std::unique_ptr<ConstantExpression> One(ScalarType::Kind kind);
+    static std::unique_ptr<ConstantExpression> ValueOf(ScalarType::Kind kind, uint64_t value);
 
-    enum ConstExprType {
-        kConstExprLiteral,
-        kConstExprUnary,
-        kConstExprBinary,
-        kConstExprTernary
+    virtual ~ConstantExpression() {}
+
+    virtual bool isReferenceConstantExpression() const;
+
+    // Proceeds recursive pass
+    // Makes sure to visit each node only once
+    // Used to provide lookup and lazy evaluation
+    status_t recursivePass(const std::function<status_t(ConstantExpression*)>& func,
+                           std::unordered_set<const ConstantExpression*>* visited,
+                           bool processBeforeDependencies);
+    status_t recursivePass(const std::function<status_t(const ConstantExpression*)>& func,
+                           std::unordered_set<const ConstantExpression*>* visited,
+                           bool processBeforeDependencies) const;
+
+    // Evaluates current constant expression
+    // Doesn't call recursive evaluation, so must be called after dependencies
+    virtual void evaluate() = 0;
+
+    std::vector<ConstantExpression*> getConstantExpressions();
+    virtual std::vector<const ConstantExpression*> getConstantExpressions() const = 0;
+
+    std::vector<Reference<LocalIdentifier>*> getReferences();
+    virtual std::vector<const Reference<LocalIdentifier>*> getReferences() const;
+
+    // Recursive tree pass checkAcyclic return type.
+    // Stores cycle end for nice error messages.
+    struct CheckAcyclicStatus {
+        CheckAcyclicStatus(status_t status, const ConstantExpression* cycleEnd = nullptr,
+                           const ReferenceConstantExpression* lastReferenceExpression = nullptr);
+
+        status_t status;
+
+        // If a cycle is found, stores the end of cycle.
+        // While going back in recursion, this is used to stop printing the cycle.
+        const ConstantExpression* cycleEnd;
+
+        // The last ReferenceConstantExpression visited on the cycle.
+        const ReferenceConstantExpression* lastReference;
     };
 
-    /* Default constructor. */
-    ConstantExpression();
-    /* Copy constructor. */
-    ConstantExpression(const ConstantExpression& other);
-    /* Copy constructor, with the expr overriden. */
-    ConstantExpression(const ConstantExpression& other, std::string expr);
-    /* Literals */
-    ConstantExpression(const char *value);
-    /* binary operations */
-    ConstantExpression(const ConstantExpression *value1,
-        const char *op, const ConstantExpression* value2);
-    /* unary operations */
-    ConstantExpression(const char *op, const ConstantExpression *value);
-    /* ternary ?: */
-    ConstantExpression(const ConstantExpression *cond,
-                       const ConstantExpression *trueVal,
-                       const ConstantExpression *falseVal);
+    // Recursive tree pass that ensures that constant expressions definitions
+    // are acyclic.
+    CheckAcyclicStatus checkAcyclic(std::unordered_set<const ConstantExpression*>* visited,
+                                    std::unordered_set<const ConstantExpression*>* stack) const;
 
-    static ConstantExpression Zero(ScalarType::Kind kind);
-    static ConstantExpression One(ScalarType::Kind kind);
-    static ConstantExpression ValueOf(ScalarType::Kind kind, uint64_t value);
-
+    /* Returns true iff the value has already been evaluated. */
+    bool isEvaluated() const;
     /* Evaluated result in a string form. */
     std::string value() const;
     /* Evaluated result in a string form. */
@@ -70,22 +105,24 @@
     std::string cppValue(ScalarType::Kind castKind) const;
     /* Evaluated result in a string form, with given contextual kind. */
     std::string javaValue(ScalarType::Kind castKind) const;
-    /* Original expression with type. */
-    const std::string &description() const;
+    /* Formatted expression with type. */
+    const std::string& description() const;
     /* See mTrivialDescription */
     bool descriptionIsTrivial() const;
     /* Return a ConstantExpression that is 1 plus the original. */
-    ConstantExpression addOne() const;
-    /* Assignment operator. */
-    ConstantExpression& operator=(const ConstantExpression& other);
+    std::unique_ptr<ConstantExpression> addOne(ScalarType::Kind baseKind);
 
     size_t castSizeT() const;
 
-private:
+    // Marks that package proceeding is completed
+    // Post parse passes must be proceeded during owner package parsin
+    void setPostParseCompleted();
+
+   private:
+    /* If the result value has been evaluated. */
+    bool mIsEvaluated = false;
     /* The formatted expression. */
     std::string mExpr;
-    /* The type of the expression. Hints on its original form. */
-    ConstExprType mType;
     /* The kind of the result value. */
     ScalarType::Kind mValueKind;
     /* The stored result value. */
@@ -93,21 +130,85 @@
     /* true if description() does not offer more information than value(). */
     bool mTrivialDescription = false;
 
+    bool mIsPostParseCompleted = false;
+
     /*
      * Helper function for all cpp/javaValue methods.
      * Returns a plain string (without any prefixes or suffixes, just the
      * digits) converted from mValue.
      */
     std::string rawValue(ScalarType::Kind castKind) const;
-    /* Trim unnecessary information. Only mValue and mValueKind is kept. */
-    ConstantExpression &toLiteral();
 
     /*
      * Return the value casted to the given type.
      * First cast it according to mValueKind, then cast it to T.
      * Assumes !containsIdentifiers()
      */
-    template <typename T> T cast() const;
+    template <typename T>
+    T cast() const;
+
+    friend struct LiteralConstantExpression;
+    friend struct UnaryConstantExpression;
+    friend struct BinaryConstantExpression;
+    friend struct TernaryConstantExpression;
+    friend struct ReferenceConstantExpression;
+};
+
+struct LiteralConstantExpression : public ConstantExpression {
+    LiteralConstantExpression(ScalarType::Kind kind, uint64_t value);
+    void evaluate() override;
+    std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+    static LiteralConstantExpression* tryParse(const std::string& value);
+
+private:
+    LiteralConstantExpression(ScalarType::Kind kind, uint64_t value, const std::string& expr);
+};
+
+struct UnaryConstantExpression : public ConstantExpression {
+    UnaryConstantExpression(const std::string& mOp, ConstantExpression* value);
+    void evaluate() override;
+    std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+   private:
+    ConstantExpression* const mUnary;
+    std::string mOp;
+};
+
+struct BinaryConstantExpression : public ConstantExpression {
+    BinaryConstantExpression(ConstantExpression* lval, const std::string& op,
+                             ConstantExpression* rval);
+    void evaluate() override;
+    std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+   private:
+    ConstantExpression* const mLval;
+    ConstantExpression* const mRval;
+    const std::string mOp;
+};
+
+struct TernaryConstantExpression : public ConstantExpression {
+    TernaryConstantExpression(ConstantExpression* cond, ConstantExpression* trueVal,
+                              ConstantExpression* falseVal);
+    void evaluate() override;
+    std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+   private:
+    ConstantExpression* const mCond;
+    ConstantExpression* const mTrueVal;
+    ConstantExpression* const mFalseVal;
+};
+
+struct ReferenceConstantExpression : public ConstantExpression {
+    ReferenceConstantExpression(const Reference<LocalIdentifier>& value, const std::string& expr);
+
+    bool isReferenceConstantExpression() const override;
+    void evaluate() override;
+    std::vector<const ConstantExpression*> getConstantExpressions() const override;
+    std::vector<const Reference<LocalIdentifier>*> getReferences() const override;
+
+   private:
+    Reference<LocalIdentifier> mReference;
 };
 
 }  // namespace android
diff --git a/Coordinator.cpp b/Coordinator.cpp
index 8c89d5e..96c9658 100644
--- a/Coordinator.cpp
+++ b/Coordinator.cpp
@@ -25,11 +25,11 @@
 #include <android-base/logging.h>
 #include <hidl-hash/Hash.h>
 #include <hidl-util/StringHelper.h>
+#include <iostream>
 
 #include "AST.h"
 #include "Interface.h"
-
-extern android::status_t parseFile(android::AST *ast);
+#include "hidl-gen_l.h"
 
 static bool existdir(const char *name) {
     DIR *dir = opendir(name);
@@ -42,40 +42,200 @@
 
 namespace android {
 
-Coordinator::Coordinator(
-        const std::vector<std::string> &packageRootPaths,
-        const std::vector<std::string> &packageRoots,
-        const std::string &rootPath)
-    : mPackageRootPaths(packageRootPaths),
-      mPackageRoots(packageRoots),
-      mRootPath(rootPath) {
-    // empty
+const std::string &Coordinator::getRootPath() const {
+    return mRootPath;
 }
 
-Coordinator::~Coordinator() {
-    // empty
-}
+void Coordinator::setRootPath(const std::string &rootPath) {
+    mRootPath = rootPath;
 
-void Coordinator::addDefaultPackagePath(const std::string& root, const std::string& path) {
-    if (std::find(mPackageRoots.begin(), mPackageRoots.end(), root) == mPackageRoots.end()) {
-        mPackageRoots.push_back(root);
-        mPackageRootPaths.push_back(path);
+    if (!mRootPath.empty() && !StringHelper::EndsWith(mRootPath, "/")) {
+        mRootPath += "/";
     }
 }
 
+void Coordinator::setOutputPath(const std::string& outputPath) {
+    mOutputPath = outputPath;
+}
+
+void Coordinator::setVerbose(bool verbose) {
+    mVerbose = verbose;
+}
+
+bool Coordinator::isVerbose() const {
+    return mVerbose;
+}
+
+void Coordinator::setDepFile(const std::string& depFile) {
+    mDepFile = depFile;
+}
+
+const std::string& Coordinator::getOwner() const {
+    return mOwner;
+}
+void Coordinator::setOwner(const std::string& owner) {
+    mOwner = owner;
+}
+
+status_t Coordinator::addPackagePath(const std::string& root, const std::string& path, std::string* error) {
+    FQName package = FQName(root, "0.0", "");
+    for (const PackageRoot &packageRoot : mPackageRoots) {
+        if (packageRoot.root.inPackage(root) || package.inPackage(packageRoot.root.package())) {
+            if (error != nullptr) {
+                *error = "ERROR: conflicting package roots " +
+                         packageRoot.root.package() +
+                         " and " +
+                         root;
+            }
+
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    mPackageRoots.push_back({path, package});
+    return OK;
+}
+void Coordinator::addDefaultPackagePath(const std::string& root, const std::string& path) {
+    addPackagePath(root, path, nullptr /* error */);
+}
+
+Formatter Coordinator::getFormatter(const FQName& fqName, Location location,
+                                    const std::string& fileName) const {
+    if (location == Location::STANDARD_OUT) {
+        return Formatter(stdout);
+    }
+
+    std::string filepath;
+    status_t err = getFilepath(fqName, location, fileName, &filepath);
+    if (err != OK) {
+        return Formatter::invalid();
+    }
+
+    onFileAccess(filepath, "w");
+
+    if (!Coordinator::MakeParentHierarchy(filepath)) {
+        fprintf(stderr, "ERROR: could not make directories for %s.\n", filepath.c_str());
+        return Formatter::invalid();
+    }
+
+    FILE* file = fopen(filepath.c_str(), "w");
+
+    if (file == nullptr) {
+        fprintf(stderr, "ERROR: could not open file %s: %d\n", filepath.c_str(), errno);
+        return Formatter::invalid();
+    }
+
+    return Formatter(file);
+}
+
+status_t Coordinator::getFilepath(const FQName& fqName, Location location,
+                                  const std::string& fileName, std::string* path) const {
+    status_t err;
+    std::string packagePath;
+    std::string packageRootPath;
+
+    switch (location) {
+        case Location::DIRECT: { /* nothing */
+            *path = mOutputPath + fileName;
+        } break;
+        case Location::PACKAGE_ROOT: {
+            err = getPackagePath(fqName, false /* relative */, false /* sanitized */, &packagePath);
+            if (err != OK) return err;
+
+            *path = mOutputPath + packagePath + fileName;
+        } break;
+        case Location::GEN_OUTPUT: {
+            err = convertPackageRootToPath(fqName, &packageRootPath);
+            if (err != OK) return err;
+            err = getPackagePath(fqName, true /* relative */, false /* sanitized */, &packagePath);
+            if (err != OK) return err;
+
+            *path = mOutputPath + packageRootPath + packagePath + fileName;
+        } break;
+        case Location::GEN_SANITIZED: {
+            err = convertPackageRootToPath(fqName, &packageRootPath);
+            if (err != OK) return err;
+            err = getPackagePath(fqName, true /* relative */, true /* sanitized */, &packagePath);
+            if (err != OK) return err;
+
+            *path = mOutputPath + packageRootPath + packagePath + fileName;
+        } break;
+        default: { CHECK(false) << "Invalid location: " << static_cast<size_t>(location); }
+    }
+
+    return OK;
+}
+
+void Coordinator::onFileAccess(const std::string& path, const std::string& mode) const {
+    if (mode == "r") {
+        // This is a global list. It's not cleared when a second fqname is processed for
+        // two reasons:
+        // 1). If there is a bug in hidl-gen, the dependencies on the first project from
+        //     the second would be required to recover correctly when the bug is fixed.
+        // 2). This option is never used in Android builds.
+        mReadFiles.insert(StringHelper::LTrim(path, mRootPath));
+    }
+
+    if (!mVerbose) {
+        return;
+    }
+
+    fprintf(stderr,
+            "VERBOSE: file access %s %s\n", path.c_str(), mode.c_str());
+}
+
+status_t Coordinator::writeDepFile(const std::string& forFile) const {
+    // No dep file requested
+    if (mDepFile.empty()) return OK;
+
+    onFileAccess(mDepFile, "w");
+
+    FILE* file = fopen(mDepFile.c_str(), "w");
+    if (file == nullptr) {
+        fprintf(stderr, "ERROR: could not open dep file at %s.\n", mDepFile.c_str());
+        return UNKNOWN_ERROR;
+    }
+
+    Formatter out(file, 2 /* spacesPerIndent */);
+    out << StringHelper::LTrim(forFile, mOutputPath) << ": \\\n";
+    out.indent([&] {
+        for (const std::string& file : mReadFiles) {
+            out << StringHelper::LTrim(file, mRootPath) << " \\\n";
+        }
+    });
+    return OK;
+}
+
 AST* Coordinator::parse(const FQName& fqName, std::set<AST*>* parsedASTs,
                         Enforce enforcement) const {
+    AST* ret;
+    status_t err = parseOptional(fqName, &ret, parsedASTs, enforcement);
+    if (err != OK) CHECK(ret == nullptr);  // internal consistency
+
+    // only in a handful of places do we want to distinguish between
+    // a missing file and a bad AST. Everywhere else, we just want to
+    // throw an error if we expect an AST to be present but it is not.
+    return ret;
+}
+
+status_t Coordinator::parseOptional(const FQName& fqName, AST** ast, std::set<AST*>* parsedASTs,
+                                    Enforce enforcement) const {
     CHECK(fqName.isFullyQualified());
 
     auto it = mCache.find(fqName);
     if (it != mCache.end()) {
-        AST *ast = (*it).second;
+        *ast = (*it).second;
 
-        if (ast != nullptr && parsedASTs != nullptr) {
-            parsedASTs->insert(ast);
+        if (*ast != nullptr && parsedASTs != nullptr) {
+            parsedASTs->insert(*ast);
         }
 
-        return ast;
+        if (*ast == nullptr) {
+            // circular import OR that AST has errors in it
+            return UNKNOWN_ERROR;
+        }
+
+        return OK;
     }
 
     // Add this to the cache immediately, so we can discover circular imports.
@@ -87,35 +247,47 @@
         // Any interface file implicitly imports its package's types.hal.
         FQName typesName = fqName.getTypesForPackage();
         // Do not enforce on imports. Do not add imports' imports to this AST.
-        typesAST = parse(typesName, nullptr, Enforce::NONE);
+        status_t err = parseOptional(typesName, &typesAST, nullptr, Enforce::NONE);
+        if (err != OK) return err;
 
         // fall through.
     }
 
-    std::string path = getAbsolutePackagePath(fqName);
+    std::string packagePath;
+    status_t err =
+        getPackagePath(fqName, false /* relative */, false /* sanitized */, &packagePath);
+    if (err != OK) return err;
 
-    path.append(fqName.name());
-    path.append(".hal");
+    const std::string path = makeAbsolute(packagePath + fqName.name() + ".hal");
 
-    AST *ast = new AST(this, path);
+    *ast = new AST(this, &Hash::getHash(path));
 
     if (typesAST != NULL) {
         // If types.hal for this AST's package existed, make it's defined
         // types available to the (about to be parsed) AST right away.
-        ast->addImportedAST(typesAST);
+        (*ast)->addImportedAST(typesAST);
     }
 
-    status_t err = parseFile(ast);
+    std::unique_ptr<FILE, std::function<void(FILE*)>> file(fopen(path.c_str(), "rb"), fclose);
 
-    if (err != OK) {
-        delete ast;
-        ast = nullptr;
-
-        return nullptr;
+    if (file == nullptr) {
+        mCache.erase(fqName);  // nullptr in cache is used to find circular imports
+        delete *ast;
+        *ast = nullptr;
+        return OK;  // File does not exist, nullptr AST* == file doesn't exist.
     }
 
-    if (ast->package().package() != fqName.package()
-            || ast->package().version() != fqName.version()) {
+    onFileAccess(path, "r");
+
+    // parse file takes ownership of file
+    if (parseFile(*ast, std::move(file)) != OK || (*ast)->postParse() != OK) {
+        delete *ast;
+        *ast = nullptr;
+        return UNKNOWN_ERROR;
+    }
+
+    if ((*ast)->package().package() != fqName.package() ||
+        (*ast)->package().version() != fqName.version()) {
         fprintf(stderr,
                 "ERROR: File at '%s' does not match expected package and/or "
                 "version.\n",
@@ -123,16 +295,15 @@
 
         err = UNKNOWN_ERROR;
     } else {
-        if (ast->isInterface()) {
+        if ((*ast)->isInterface()) {
             if (fqName.name() == "types") {
                 fprintf(stderr,
                         "ERROR: File at '%s' declares an interface '%s' "
                         "instead of the expected types common to the package.\n",
-                        path.c_str(),
-                        ast->getInterface()->localName().c_str());
+                        path.c_str(), (*ast)->getInterface()->localName().c_str());
 
                 err = UNKNOWN_ERROR;
-            } else if (ast->getInterface()->localName() != fqName.name()) {
+            } else if ((*ast)->getInterface()->localName() != fqName.name()) {
                 fprintf(stderr,
                         "ERROR: File at '%s' does not declare interface type "
                         "'%s'.\n",
@@ -149,7 +320,7 @@
                     fqName.name().c_str());
 
             err = UNKNOWN_ERROR;
-        } else if (ast->containsInterfaces()) {
+        } else if ((*ast)->containsInterfaces()) {
             fprintf(stderr,
                     "ERROR: types.hal file at '%s' declares at least one "
                     "interface type.\n",
@@ -160,32 +331,32 @@
     }
 
     if (err != OK) {
-        delete ast;
-        ast = nullptr;
-
-        return nullptr;
+        delete *ast;
+        *ast = nullptr;
+        return err;
     }
 
-    if (parsedASTs != nullptr) { parsedASTs->insert(ast); }
+    if (parsedASTs != nullptr) {
+        parsedASTs->insert(*ast);
+    }
 
     // put it into the cache now, so that enforceRestrictionsOnPackage can
     // parse fqName.
-    mCache[fqName] = ast;
+    mCache[fqName] = *ast;
 
     // For each .hal file that hidl-gen parses, the whole package will be checked.
     err = enforceRestrictionsOnPackage(fqName, enforcement);
     if (err != OK) {
         mCache[fqName] = nullptr;
-        delete ast;
-        ast = nullptr;
-        return nullptr;
+        delete *ast;
+        *ast = nullptr;
+        return err;
     }
 
-    return ast;
+    return OK;
 }
 
-std::vector<std::string>::const_iterator
-Coordinator::findPackageRoot(const FQName &fqName) const {
+const Coordinator::PackageRoot* Coordinator::findPackageRoot(const FQName& fqName) const {
     CHECK(!fqName.package().empty());
 
     // Find the right package prefix and path for this FQName.  For
@@ -195,92 +366,77 @@
     // prefix "android.hardware" and the package root
     // "hardware/interfaces".
 
-    auto it = mPackageRoots.begin();
     auto ret = mPackageRoots.end();
-    for (; it != mPackageRoots.end(); it++) {
-        if (!fqName.inPackage(*it)) {
+    for (auto it = mPackageRoots.begin(); it != mPackageRoots.end(); it++) {
+        if (!fqName.inPackage(it->root.package())) {
             continue;
         }
 
-        CHECK(ret == mPackageRoots.end())
-            << "Multiple package roots found for " << fqName.string()
-            << " (" << *it << " and " << *ret << ")";
+        if (ret != mPackageRoots.end()) {
+            std::cerr << "ERROR: Multiple package roots found for " << fqName.string() << " ("
+                      << it->root.package() << " and " << ret->root.package() << ")\n";
+            return nullptr;
+        }
 
         ret = it;
     }
-    CHECK(ret != mPackageRoots.end())
-        << "Unable to find package root for " << fqName.string();
 
-    return ret;
-}
-
-std::string Coordinator::getAbsolutePackagePath(const FQName& fqName) const {
-    const std::string packagePath = getPackagePath(fqName);
-
-    if (StringHelper::StartsWith(packagePath, "/") || mRootPath.empty()) {
-        return packagePath;
+    if (ret == mPackageRoots.end()) {
+        std::cerr << "ERROR: Package root not specified for " << fqName.string() << "\n";
+        return nullptr;
     }
 
-    return StringHelper::RTrim(mRootPath, "/") + "/" + packagePath;
+    return &(*ret);
 }
 
-std::string Coordinator::getPackageRoot(const FQName &fqName) const {
-    auto it = findPackageRoot(fqName);
-    auto prefix = *it;
-    return prefix;
-}
-
-std::string Coordinator::getPackageRootPath(const FQName &fqName) const {
-    auto it = findPackageRoot(fqName);
-    auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
-    return root;
-}
-
-std::string Coordinator::getPackageRootOption(const FQName &fqName) const {
-    return getPackageRoot(fqName) + ":" + getPackageRootPath(fqName);
-}
-
-std::string Coordinator::getPackagePath(
-        const FQName &fqName, bool relative, bool sanitized) const {
-
-    auto it = findPackageRoot(fqName);
-    auto prefix = *it;
-    auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
-
-    // Make sure the prefix ends on a '.' and the root path on a '/'
-    if ((*--prefix.end()) != '.') {
-        prefix += '.';
+std::string Coordinator::makeAbsolute(const std::string& path) const {
+    if (StringHelper::StartsWith(path, "/") || mRootPath.empty()) {
+        return path;
     }
 
-    if ((*--root.end()) != '/') {
-        root += '/';
+    return mRootPath + path;
+}
+
+status_t Coordinator::getPackageRoot(const FQName& fqName, std::string* root) const {
+    const PackageRoot* packageRoot = findPackageRoot(fqName);
+    if (root == nullptr) {
+        return UNKNOWN_ERROR;
     }
+    *root = packageRoot->root.package();
+    return OK;
+}
 
-    // Given FQName of "android.hardware.nfc@1.0::IFoo" and a prefix
-    // "android.hardware.", the suffix is "nfc@1.0::IFoo".
-    const std::string packageSuffix = fqName.package().substr(prefix.length());
+status_t Coordinator::getPackageRootPath(const FQName& fqName, std::string* path) const {
+    const PackageRoot* packageRoot = findPackageRoot(fqName);
+    if (packageRoot == nullptr) {
+        return UNKNOWN_ERROR;
+    }
+    *path = packageRoot->path;
+    return OK;
+}
 
-    std::string packagePath;
+status_t Coordinator::getPackagePath(const FQName& fqName, bool relative, bool sanitized,
+                                     std::string* path) const {
+    const PackageRoot* packageRoot = findPackageRoot(fqName);
+    if (packageRoot == nullptr) return UNKNOWN_ERROR;
+
+    // Given FQName of "android.hardware.nfc.test@1.0::IFoo" and a prefix
+    // "android.hardware", the suffix is "nfc.test".
+    std::string suffix = StringHelper::LTrim(fqName.package(), packageRoot->root.package());
+    suffix = StringHelper::LTrim(suffix, ".");
+
+    std::vector<std::string> suffixComponents;
+    StringHelper::SplitString(suffix, '.', &suffixComponents);
+
+    std::vector<std::string> components;
     if (!relative) {
-        packagePath = root;
+        components.push_back(StringHelper::RTrimAll(packageRoot->path, "/"));
     }
+    components.insert(components.end(), suffixComponents.begin(), suffixComponents.end());
+    components.push_back(sanitized ? fqName.sanitizedVersion() : fqName.version());
 
-    size_t startPos = 0;
-    size_t dotPos;
-    while ((dotPos = packageSuffix.find('.', startPos)) != std::string::npos) {
-        packagePath.append(packageSuffix.substr(startPos, dotPos - startPos));
-        packagePath.append("/");
-
-        startPos = dotPos + 1;
-    }
-    CHECK_LT(startPos + 1, packageSuffix.length());
-    packagePath.append(packageSuffix.substr(startPos));
-    packagePath.append("/");
-
-    packagePath.append(sanitized ? fqName.sanitizedVersion() : fqName.version());
-    packagePath.append("/");
-
-    return packagePath;
+    *path = StringHelper::JoinStrings(components, "/") + "/";
+    return OK;
 }
 
 status_t Coordinator::getPackageInterfaceFiles(
@@ -288,16 +444,17 @@
         std::vector<std::string> *fileNames) const {
     fileNames->clear();
 
-    const std::string packagePath = getAbsolutePackagePath(package);
+    std::string packagePath;
+    status_t err =
+        getPackagePath(package, false /* relative */, false /* sanitized */, &packagePath);
+    if (err != OK) return err;
 
-    DIR *dir = opendir(packagePath.c_str());
+    const std::string path = makeAbsolute(packagePath);
+    DIR* dir = opendir(path.c_str());
 
     if (dir == NULL) {
-        fprintf(stderr,
-                "ERROR: Could not open package path %s for package %s:\n%s\n",
-                getPackagePath(package).c_str(),
-                package.string().c_str(),
-                packagePath.c_str());
+        fprintf(stderr, "ERROR: Could not open package path %s for package %s:\n%s\n",
+                packagePath.c_str(), package.string().c_str(), path.c_str());
         return -errno;
     }
 
@@ -349,28 +506,17 @@
     }
 
     for (const auto &fileName : fileNames) {
-        FQName subFQName(
-                package.package() + package.atVersion() + "::" + fileName);
-
-        if (!subFQName.isValid()) {
-            LOG(WARNING)
-                << "Whole-package import encountered invalid filename '"
-                << fileName
-                << "' in package "
-                << package.package()
-                << package.atVersion();
-
-            continue;
-        }
-
+        FQName subFQName(package.package(), package.version(), fileName);
         packageInterfaces->push_back(subFQName);
     }
 
     return OK;
 }
 
-std::string Coordinator::convertPackageRootToPath(const FQName &fqName) const {
-    std::string packageRoot = getPackageRoot(fqName);
+status_t Coordinator::convertPackageRootToPath(const FQName& fqName, std::string* path) const {
+    std::string packageRoot;
+    status_t err = getPackageRoot(fqName, &packageRoot);
+    if (err != OK) return err;
 
     if (*(packageRoot.end()--) != '.') {
         packageRoot += '.';
@@ -378,7 +524,85 @@
 
     std::replace(packageRoot.begin(), packageRoot.end(), '.', '/');
 
-    return packageRoot; // now converted to a path
+    *path = packageRoot;  // now converted to a path
+    return OK;
+}
+
+status_t Coordinator::isTypesOnlyPackage(const FQName& package, bool* result) const {
+    std::vector<FQName> packageInterfaces;
+
+    status_t err = appendPackageInterfacesToVector(package, &packageInterfaces);
+
+    if (err != OK) {
+        *result = false;
+        return err;
+    }
+
+    *result = packageInterfaces.size() == 1 && packageInterfaces[0].name() == "types";
+    return OK;
+}
+
+status_t Coordinator::addUnreferencedTypes(const std::vector<FQName>& packageInterfaces,
+                                           std::set<FQName>* unreferencedDefinitions,
+                                           std::set<FQName>* unreferencedImports) const {
+    CHECK(unreferencedDefinitions != nullptr);
+    CHECK(unreferencedImports != nullptr);
+
+    std::set<FQName> packageDefinedTypes;
+    std::set<FQName> packageReferencedTypes;
+    std::set<FQName> packageImportedTypes;
+    std::set<FQName> typesDefinedTypes;  // only types.hal types
+
+    for (const auto& fqName : packageInterfaces) {
+        AST* ast = parse(fqName);
+        if (!ast) {
+            std::cerr << "ERROR: Could not parse " << fqName.string() << ". Aborting." << std::endl;
+
+            return UNKNOWN_ERROR;
+        }
+
+        ast->addDefinedTypes(&packageDefinedTypes);
+        ast->addReferencedTypes(&packageReferencedTypes);
+        ast->getAllImportedNamesGranular(&packageImportedTypes);
+
+        if (fqName.name() == "types") {
+            ast->addDefinedTypes(&typesDefinedTypes);
+        }
+    }
+
+#if 0
+    for (const auto &fqName : packageDefinedTypes) {
+        std::cout << "VERBOSE: DEFINED " << fqName.string() << std::endl;
+    }
+
+    for (const auto &fqName : packageImportedTypes) {
+        std::cout << "VERBOSE: IMPORTED " << fqName.string() << std::endl;
+    }
+
+    for (const auto &fqName : packageReferencedTypes) {
+        std::cout << "VERBOSE: REFERENCED " << fqName.string() << std::endl;
+    }
+
+    for (const auto &fqName : typesDefinedTypes) {
+        std::cout << "VERBOSE: DEFINED in types.hal " << fqName.string() << std::endl;
+    }
+#endif
+
+    for (const auto& fqName : packageReferencedTypes) {
+        packageDefinedTypes.erase(fqName);
+        packageImportedTypes.erase(fqName);
+    }
+
+    // A package implicitly imports its own types.hal, only track them in one set.
+    for (const auto& fqName : typesDefinedTypes) {
+        packageImportedTypes.erase(fqName);
+    }
+
+    // defined but not referenced
+    unreferencedDefinitions->insert(packageDefinedTypes.begin(), packageDefinedTypes.end());
+    // imported but not referenced
+    unreferencedImports->insert(packageImportedTypes.begin(), packageImportedTypes.end());
+    return OK;
 }
 
 status_t Coordinator::enforceRestrictionsOnPackage(const FQName& fqName,
@@ -389,8 +613,8 @@
     // need fqName to be something like android.hardware.foo@1.0.
     // name and valueName is ignored.
     if (fqName.package().empty() || fqName.version().empty()) {
-        LOG(ERROR) << "Cannot enforce restrictions on package " << fqName.string()
-                   << ": package or version is missing.";
+        std::cerr << "ERROR: Cannot enforce restrictions on package " << fqName.string()
+                  << ": package or version is missing." << std::endl;
         return BAD_VALUE;
     }
 
@@ -407,7 +631,7 @@
     // enforce all rules.
     status_t err;
 
-    err = enforceMinorVersionUprevs(package);
+    err = enforceMinorVersionUprevs(package, enforcement);
     if (err != OK) {
         return err;
     }
@@ -424,10 +648,11 @@
     return OK;
 }
 
-status_t Coordinator::enforceMinorVersionUprevs(const FQName &currentPackage) const {
+status_t Coordinator::enforceMinorVersionUprevs(const FQName& currentPackage,
+                                                Enforce enforcement) const {
     if(!currentPackage.hasVersion()) {
-        LOG(ERROR) << "Cannot enforce minor version uprevs for " << currentPackage.string()
-                   << ": missing version.";
+        std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string()
+                  << ": missing version." << std::endl;
         return UNKNOWN_ERROR;
     }
 
@@ -439,7 +664,13 @@
     FQName prevPackage = currentPackage;
     while (prevPackage.getPackageMinorVersion() > 0) {
         prevPackage = prevPackage.downRev();
-        if (existdir(getAbsolutePackagePath(prevPackage).c_str())) {
+
+        std::string prevPackagePath;
+        status_t err = getPackagePath(prevPackage, false /* relative */, false /* sanitized */,
+                                      &prevPackagePath);
+        if (err != OK) return err;
+
+        if (existdir(makeAbsolute(prevPackagePath).c_str())) {
             hasPrevPackage = true;
             break;
         }
@@ -450,13 +681,22 @@
     }
 
     if (prevPackage != currentPackage.downRev()) {
-        LOG(ERROR) << "Cannot enforce minor version uprevs for " << currentPackage.string()
-                   << ": Found package " << prevPackage.string() << " but missing "
-                   << currentPackage.downRev().string() << "; you cannot skip a minor version.";
+        std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string()
+                  << ": Found package " << prevPackage.string() << " but missing "
+                  << currentPackage.downRev().string() << "; you cannot skip a minor version."
+                  << std::endl;
         return UNKNOWN_ERROR;
     }
 
-    status_t err;
+    bool prevIsTypesOnly;
+    status_t err = isTypesOnlyPackage(prevPackage, &prevIsTypesOnly);
+    if (err != OK) return err;
+
+    if (prevIsTypesOnly) {
+        // A types only package can be extended in any way.
+        return OK;
+    }
+
     std::vector<FQName> packageInterfaces;
     err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
     if (err != OK) {
@@ -470,19 +710,20 @@
         }
 
         const Interface *iface = nullptr;
-        AST *currentAST = parse(currentFQName);
+        AST* currentAST = parse(currentFQName, nullptr /* parsedASTs */, enforcement);
         if (currentAST != nullptr) {
             iface = currentAST->getInterface();
         }
         if (iface == nullptr) {
             if (currentAST == nullptr) {
-                LOG(WARNING) << "Warning: Skipping " << currentFQName.string()
-                             << " because it could not be found or parsed"
-                             << " or " << currentPackage.string()
-                             << " doesn't pass all requirements.";
+                std::cerr << "WARNING: Skipping " << currentFQName.string()
+                          << " because it could not be found or parsed"
+                          << " or " << currentPackage.string() << " doesn't pass all requirements."
+                          << std::endl;
             } else {
-                LOG(WARNING) << "Warning: Skipping " << currentFQName.string()
-                             << " because the file might contain more than one interface.";
+                std::cerr << "WARNING: Skipping " << currentFQName.string()
+                          << " because the file might contain more than one interface."
+                          << std::endl;
             }
             continue;
         }
@@ -508,73 +749,144 @@
 
         bool lastFQNameExists = lastAST != nullptr && lastAST->getInterface() != nullptr;
 
-        if (iface->superType()->fqName() != lastFQName && lastFQNameExists) {
-            LOG(ERROR) << "Cannot enforce minor version uprevs for " << currentPackage.string()
-                       << ": " << iface->fqName().string() << " extends "
-                       << iface->superType()->fqName().string()
-                       << ", which is not allowed. It must extend " << lastFQName.string();
+        if (!lastFQNameExists) {
+            continue;
+        }
+
+        if (iface->superType()->fqName() != lastFQName) {
+            std::cerr << "ERROR: Cannot enforce minor version uprevs for "
+                      << currentPackage.string() << ": " << iface->fqName().string() << " extends "
+                      << iface->superType()->fqName().string()
+                      << ", which is not allowed. It must extend " << lastFQName.string()
+                      << std::endl;
             return UNKNOWN_ERROR;
         }
 
         // at least one interface must extend the previous version
+        // @2.0::IFoo does not work. It must be @2.1::IFoo for at least one interface.
         if (lastFQName.getPackageAndVersion() == prevPackage.getPackageAndVersion()) {
             extendedInterface = true;
         }
 
-        LOG(VERBOSE) << "enforceMinorVersionUprevs: " << currentFQName.string() << " passes.";
+        if (mVerbose) {
+            std::cout << "VERBOSE: EnforceMinorVersionUprevs: " << currentFQName.string()
+                      << " passes." << std::endl;
+        }
     }
 
     if (!extendedInterface) {
         // No interface extends the interface with the same name in @x.(y-1).
-        LOG(ERROR) << currentPackage.string() << " doesn't pass minor version uprev requirement. "
-                   << "Requires at least one interface to extend an interface with the same name "
-                   << "from " << prevPackage.string() << ".";
+        std::cerr << "ERROR: " << currentPackage.string()
+                  << " doesn't pass minor version uprev requirement. "
+                  << "Requires at least one interface to extend an interface with the same name "
+                  << "from " << prevPackage.string() << "." << std::endl;
         return UNKNOWN_ERROR;
     }
 
     return OK;
 }
 
-status_t Coordinator::enforceHashes(const FQName &currentPackage) const {
-    status_t err = OK;
+Coordinator::HashStatus Coordinator::checkHash(const FQName& fqName) const {
+    AST* ast = parse(fqName);
+    if (ast == nullptr) return HashStatus::ERROR;
+
+    std::string rootPath;
+    status_t err = getPackageRootPath(fqName, &rootPath);
+    if (err != OK) return HashStatus::ERROR;
+
+    std::string hashPath = makeAbsolute(rootPath) + "/current.txt";
+    std::string error;
+    bool fileExists;
+    std::vector<std::string> frozen =
+        Hash::lookupHash(hashPath, fqName.string(), &error, &fileExists);
+    if (fileExists) onFileAccess(hashPath, "r");
+
+    if (error.size() > 0) {
+        std::cerr << "ERROR: " << error << std::endl;
+        return HashStatus::ERROR;
+    }
+
+    // hash not defined, interface not frozen
+    if (frozen.size() == 0) {
+        // This ensures that it can be detected.
+        Hash::clearHash(ast->getFilename());
+
+        return HashStatus::UNFROZEN;
+    }
+
+    std::string currentHash = ast->getFileHash()->hexString();
+
+    if (std::find(frozen.begin(), frozen.end(), currentHash) == frozen.end()) {
+        std::cerr << "ERROR: " << fqName.string() << " has hash " << currentHash
+                  << " which does not match hash on record. This interface has "
+                  << "been frozen. Do not change it!" << std::endl;
+        return HashStatus::CHANGED;
+    }
+
+    return HashStatus::FROZEN;
+}
+
+status_t Coordinator::getUnfrozenDependencies(const FQName& fqName,
+                                              std::set<FQName>* result) const {
+    CHECK(result != nullptr);
+
+    AST* ast = parse(fqName);
+    if (ast == nullptr) return UNKNOWN_ERROR;
+
+    std::set<FQName> imported;
+    ast->getImportedPackages(&imported);
+
+    // no circular dependency is already guaranteed by parsing
+    // indirect dependencies will be checked when the imported interface frozen checks are done
+    for (const FQName& importedPackage : imported) {
+        std::vector<FQName> packageInterfaces;
+        status_t err = appendPackageInterfacesToVector(importedPackage, &packageInterfaces);
+        if (err != OK) {
+            return err;
+        }
+
+        for (const FQName& importedName : packageInterfaces) {
+            HashStatus status = checkHash(importedName);
+            if (status == HashStatus::ERROR) return UNKNOWN_ERROR;
+            if (status == HashStatus::UNFROZEN) {
+                result->insert(importedName);
+            }
+        }
+    }
+
+    return OK;
+}
+
+status_t Coordinator::enforceHashes(const FQName& currentPackage) const {
     std::vector<FQName> packageInterfaces;
-    err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
+    status_t err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
     if (err != OK) {
         return err;
     }
 
-    for (const FQName &currentFQName : packageInterfaces) {
-        AST *ast = parse(currentFQName);
+    for (const FQName& currentFQName : packageInterfaces) {
+        HashStatus status = checkHash(currentFQName);
 
-        if (ast == nullptr) {
-            err = UNKNOWN_ERROR;
-            continue;
+        if (status == HashStatus::ERROR) return UNKNOWN_ERROR;
+        if (status == HashStatus::CHANGED) return UNKNOWN_ERROR;
+
+        // frozen interface can only depend on a frozen interface
+        if (status == HashStatus::FROZEN) {
+            std::set<FQName> unfrozenDependencies;
+            err = getUnfrozenDependencies(currentFQName, &unfrozenDependencies);
+            if (err != OK) return err;
+
+            if (!unfrozenDependencies.empty()) {
+                std::cerr << "ERROR: Frozen interface " << currentFQName.string()
+                          << " cannot depend on unfrozen thing(s):" << std::endl;
+                for (const FQName& name : unfrozenDependencies) {
+                    std::cerr << " (unfrozen) " << name.string() << std::endl;
+                }
+                return UNKNOWN_ERROR;
+            }
         }
 
-        std::string hashPath = getPackageRootPath(currentFQName) + "/current.txt";
-        std::string error;
-        std::vector<std::string> frozen = Hash::lookupHash(hashPath, currentFQName.string(), &error);
-
-        if (error.size() > 0) {
-            LOG(ERROR) << error;
-            err = UNKNOWN_ERROR;
-            continue;
-        }
-
-        // hash not define, interface not frozen
-        if (frozen.size() == 0) {
-            continue;
-        }
-
-        std::string currentHash = Hash::getHash(ast->getFilename()).hexString();
-
-        if(std::find(frozen.begin(), frozen.end(), currentHash) == frozen.end()) {
-            LOG(ERROR) << currentFQName.string() << " has hash " << currentHash
-                       << " which does not match hash on record. This interface has "
-                       << "been frozen. Do not change it!";
-            err = UNKNOWN_ERROR;
-            continue;
-        }
+        // UNFROZEN, ignore
     }
 
     return err;
@@ -585,7 +897,7 @@
 
     size_t start = 1;  // Ignore leading '/'
     size_t slashPos;
-    while ((slashPos = path.find("/", start)) != std::string::npos) {
+    while ((slashPos = path.find('/', start)) != std::string::npos) {
         std::string partial = path.substr(0, slashPos);
 
         struct stat st;
diff --git a/Coordinator.h b/Coordinator.h
index 2e761df..2dd1ca3 100644
--- a/Coordinator.h
+++ b/Coordinator.h
@@ -19,30 +19,58 @@
 #define COORDINATOR_H_
 
 #include <android-base/macros.h>
-#include <functional>
+#include <hidl-util/FQName.h>
+#include <hidl-util/Formatter.h>
+#include <utils/Errors.h>
 #include <map>
 #include <set>
 #include <string>
-#include <utils/Errors.h>
 #include <vector>
 
 namespace android {
 
 struct AST;
-struct FQName;
 struct Type;
 
 struct Coordinator {
-    Coordinator(
-            const std::vector<std::string> &packageRootPaths,
-            const std::vector<std::string> &packageRoots,
-            const std::string &rootPath);
+    Coordinator() {};
 
-    ~Coordinator();
+    const std::string& getRootPath() const;
+    void setRootPath(const std::string &rootPath);
+    void setOutputPath(const std::string& outputPath);
+
+    void setVerbose(bool value);
+    bool isVerbose() const;
+
+    void setDepFile(const std::string& depFile);
+
+    const std::string& getOwner() const;
+    void setOwner(const std::string& owner);
 
     // adds path only if it doesn't exist
+    status_t addPackagePath(const std::string& root, const std::string& path, std::string* error);
+    // adds path if it hasn't already been added
     void addDefaultPackagePath(const std::string& root, const std::string& path);
 
+    enum class Location {
+        STANDARD_OUT,
+        DIRECT,         // mOutputPath + file name
+        PACKAGE_ROOT,   // e.x. mRootPath + /nfc/1.0/Android.bp
+        GEN_OUTPUT,     // e.x. mOutputPath + /android/hardware/foo/1.0/*.cpp
+        GEN_SANITIZED,  // e.x. mOutputPath + /android/hardware/foo/V1_0/*.cpp
+    };
+
+    status_t getFilepath(const FQName& fqName, Location location, const std::string& fileName,
+                         std::string* path) const;
+
+    Formatter getFormatter(const FQName& fqName, Location location,
+                           const std::string& fileName) const;
+
+    // must be called before file access
+    void onFileAccess(const std::string& path, const std::string& mode) const;
+
+    status_t writeDepFile(const std::string& forFile) const;
+
     enum class Enforce {
         FULL,     // default
         NO_HASH,  // only for use with -Lhash
@@ -58,35 +86,29 @@
     AST* parse(const FQName& fqName, std::set<AST*>* parsedASTs = nullptr,
                Enforce enforcement = Enforce::FULL) const;
 
+    // Same as parse, but it distinguishes between "missing file" and "could not parse AST"
+    // return OK, out *ast:
+    //    0xdeadbeef -> successfully parsed
+    //    nullptr    -> file not present
+    // return !OK
+    //    could not parse AST and file exists
+    status_t parseOptional(const FQName& fqName, AST** ast, std::set<AST*>* parsedASTs = nullptr,
+                           Enforce enforcement = Enforce::FULL) const;
+
     // Given package-root paths of ["hardware/interfaces",
     // "vendor/<something>/interfaces"], package roots of
     // ["android.hardware", "vendor.<something>.hardware"], and a
     // FQName of "android.hardware.nfc@1.0::INfc, then getPackagePath()
     // will return "hardware/interfaces/nfc/1.0" (if sanitized = false)
     // or "hardware/interfaces/nfc/V1_0" (if sanitized = true).
-    std::string getPackagePath(
-            const FQName &fqName, bool relative = false,
-            bool sanitized = false) const;
+    status_t getPackagePath(const FQName& fqName, bool relative, bool sanitized,
+                            std::string* path) const;
 
     // Given package roots of ["android.hardware",
     // "vendor.<something>.hardware"] and a FQName of
     // "android.hardware.nfc@1.0::INfc, then getPackageRoot() will
     // return "android.hardware".
-    std::string getPackageRoot(const FQName &fqName) const;
-
-    // Given package-root paths of ["hardware/interfaces",
-    // "vendor/<something>/interfaces"], package roots of
-    // ["android.hardware", "vendor.<something>.hardware"], and a
-    // FQName of "android.hardware.nfc@1.0::INfc, then getPackageRootPath()
-    // will return "hardware/interfaces".
-    std::string getPackageRootPath(const FQName &fqName) const;
-
-    // return getPackageRoot + ":" + getPackageRootPath
-    std::string getPackageRootOption(const FQName &fqName) const;
-
-    // Given an FQName of "android.hardware.nfc@1.0::INfc", return
-    // "android/hardware/".
-    std::string convertPackageRootToPath(const FQName &fqName) const;
+    status_t getPackageRoot(const FQName& fqName, std::string* root) const;
 
     status_t getPackageInterfaceFiles(
             const FQName &package,
@@ -96,6 +118,13 @@
             const FQName &package,
             std::vector<FQName> *packageInterfaces) const;
 
+    status_t isTypesOnlyPackage(const FQName& package, bool* result) const;
+
+    // Returns types which are imported/defined but not referenced in code
+    status_t addUnreferencedTypes(const std::vector<FQName>& packageInterfaces,
+                                  std::set<FQName>* unreferencedDefinitions,
+                                  std::set<FQName>* unreferencedImports) const;
+
     // Enforce a set of restrictions on a set of packages. These include:
     //    - minor version upgrades
     // "packages" contains names like "android.hardware.nfc@1.1".
@@ -103,19 +132,46 @@
     status_t enforceRestrictionsOnPackage(const FQName& fqName,
                                           Enforce enforcement = Enforce::FULL) const;
 
+private:
     static bool MakeParentHierarchy(const std::string &path);
 
-private:
-    // A list of top-level directories (mPackageRootPaths)
-    // corresponding to a list of package roots (mPackageRoots). For
-    // example, if mPackageRootPaths[0] == "hardware/interfaces" and
-    // mPackageRoots[0] == "android.hardware" this means that all
-    // packages starting with "android.hardware" will be looked up in
-    // "hardware/interfaces".
-    std::vector<std::string> mPackageRootPaths;
-    std::vector<std::string> mPackageRoots;
+    enum class HashStatus {
+        ERROR,
+        UNFROZEN,
+        FROZEN,
+        CHANGED,  // frozen but changed
+    };
+    HashStatus checkHash(const FQName& fqName) const;
+    status_t getUnfrozenDependencies(const FQName& fqName, std::set<FQName>* result) const;
 
-    std::string mRootPath;
+    // indicates that packages in "android.hardware" will be looked up in hardware/interfaces
+    struct PackageRoot {
+        std::string path; // e.x. hardware/interfaces
+        FQName root; // e.x. android.hardware@0.0
+    };
+
+    // nullptr if it doesn't exist
+    const PackageRoot* findPackageRoot(const FQName& fqName) const;
+
+    // Given package-root paths of ["hardware/interfaces",
+    // "vendor/<something>/interfaces"], package roots of
+    // ["android.hardware", "vendor.<something>.hardware"], and a
+    // FQName of "android.hardware.nfc@1.0::INfc, then getPackageRootPath()
+    // will return "hardware/interfaces".
+    status_t getPackageRootPath(const FQName& fqName, std::string* path) const;
+
+    // Given an FQName of "android.hardware.nfc@1.0::INfc", return
+    // "android/hardware/".
+    status_t convertPackageRootToPath(const FQName& fqName, std::string* path) const;
+
+    std::vector<PackageRoot> mPackageRoots;
+    std::string mRootPath;    // root of android source tree (to locate package roots)
+    std::string mOutputPath;  // root of output directory
+    std::string mDepFile;     // location to write depfile
+
+    // hidl-gen options
+    bool mVerbose = false;
+    std::string mOwner;
 
     // cache to parse().
     mutable std::map<FQName, AST *> mCache;
@@ -123,19 +179,14 @@
     // cache to enforceRestrictionsOnPackage().
     mutable std::set<FQName> mPackagesEnforced;
 
-    std::vector<std::string>::const_iterator findPackageRoot(
-            const FQName &fqName) const;
+    mutable std::set<std::string> mReadFiles;
 
-    // Returns abs package path by prepending the root path if a package
-    // path is non-absolute.
-    // If root is '/android/master' and getPackagePath returns 'h/i/nfc/V1_0'
-    // this will return '/android/master/h/i/nfc/V1_0'.
-    // If root is '/android/master' and getPackagePath returns '/abs/path/to/nfc/V1_0'
-    // this will return '/abs/path/to/nfc/V1_0'
-    std::string getAbsolutePackagePath(const FQName& fqName) const;
+    // Returns the given path if it is absolute, otherwise it returns
+    // the path relative to mRootPath
+    std::string makeAbsolute(const std::string& string) const;
 
     // Rules of enforceRestrictionsOnPackage are listed below.
-    status_t enforceMinorVersionUprevs(const FQName &fqName) const;
+    status_t enforceMinorVersionUprevs(const FQName& fqName, Enforce enforcement) const;
     status_t enforceHashes(const FQName &fqName) const;
 
     DISALLOW_COPY_AND_ASSIGN(Coordinator);
diff --git a/DeathRecipientType.cpp b/DeathRecipientType.cpp
index 828f217..abc69fc 100644
--- a/DeathRecipientType.cpp
+++ b/DeathRecipientType.cpp
@@ -21,7 +21,7 @@
 
 namespace android {
 
-DeathRecipientType::DeathRecipientType() {}
+DeathRecipientType::DeathRecipientType(Scope* parent) : Type(parent) {}
 
 std::string DeathRecipientType::typeName() const {
     return "death recipient";
@@ -74,17 +74,12 @@
     return true;
 }
 
-bool DeathRecipientType::isJavaCompatible() const {
-    return true;
-}
-
 void DeathRecipientType::getAlignmentAndSize(size_t *align, size_t *size) const {
     *align = *size = 0; // this object should only be used in passthrough mode
 }
 
-status_t DeathRecipientType::emitVtsTypeDeclarations(Formatter &out) const {
+void DeathRecipientType::emitVtsTypeDeclarations(Formatter& out) const {
     out << "type: " << getVtsType() << "\n";
-    return OK;
 }
 
 }  // namespace android
diff --git a/DeathRecipientType.h b/DeathRecipientType.h
index 386cec5..a7213cd 100644
--- a/DeathRecipientType.h
+++ b/DeathRecipientType.h
@@ -23,7 +23,7 @@
 namespace android {
 
 struct DeathRecipientType : public Type {
-    DeathRecipientType();
+    DeathRecipientType(Scope* parent);
 
     std::string getCppType(
             StorageMode mode,
@@ -45,11 +45,9 @@
     bool needsEmbeddedReadWrite() const override;
     bool resultNeedsDeref() const override;
 
-    bool isJavaCompatible() const override;
-
     void getAlignmentAndSize(size_t *align, size_t *size) const override;
 
-    status_t emitVtsTypeDeclarations(Formatter &out) const override;
+    void emitVtsTypeDeclarations(Formatter& out) const override;
 };
 
 }  // namespace android
diff --git a/DocComment.cpp b/DocComment.cpp
new file mode 100644
index 0000000..91bc676
--- /dev/null
+++ b/DocComment.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DocComment.h"
+
+#include <hidl-util/StringHelper.h>
+
+#include <cctype>
+#include <sstream>
+
+namespace android {
+
+DocComment::DocComment(const std::string& comment) {
+    std::vector<std::string> lines;
+    StringHelper::SplitString(comment, '\n', &lines);
+
+    bool foundFirstLine = false;
+
+    std::ostringstream is;
+    for (size_t l = 0; l < lines.size(); l++) {
+        const std::string& line = lines[l];
+
+        // Delete prefixes like "    * ", "   *", or "    ".
+        size_t idx = 0;
+        for (; idx < line.size() && isspace(line[idx]); idx++)
+            ;
+        if (idx < line.size() && line[idx] == '*') idx++;
+        if (idx < line.size() && line[idx] == ' ') idx++;
+
+        if (idx < line.size()) {
+            foundFirstLine = true;
+        }
+
+        if (!foundFirstLine) continue;
+
+        is << line.substr(idx);
+
+        if (l + 1 < lines.size()) {
+            is << "\n";
+        }
+    }
+
+    mComment = is.str();
+}
+
+void DocComment::merge(const DocComment* comment) {
+    mComment = mComment + "\n\n" + comment->mComment;
+}
+
+void DocComment::emit(Formatter& out) const {
+    out << "/**\n";
+    out.setLinePrefix(" * ");
+    out << mComment;
+    out.unsetLinePrefix();
+    out << " */\n";
+}
+
+}  // namespace android
diff --git a/DocComment.h b/DocComment.h
new file mode 100644
index 0000000..e0677eb
--- /dev/null
+++ b/DocComment.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DOC_COMMENT_H_
+
+#define DOC_COMMENT_H_
+
+#include <hidl-util/Formatter.h>
+
+#include <string>
+
+namespace android {
+
+struct DocComment {
+    DocComment(const std::string& comment);
+
+    void merge(const DocComment* comment);
+
+    void emit(Formatter& out) const;
+
+   private:
+    std::string mComment;
+};
+
+struct DocCommentable {
+    void setDocComment(const DocComment* docComment) { mDocComment = docComment; }
+    void emitDocComment(Formatter& out) const {
+        if (mDocComment != nullptr) {
+            mDocComment->emit(out);
+        }
+    }
+
+   private:
+    const DocComment* mDocComment = nullptr;
+};
+
+}  // namespace android
+
+#endif  // DOC_COMMENT_H_
diff --git a/EnumType.cpp b/EnumType.cpp
index fb8545f..ead64ba 100644
--- a/EnumType.cpp
+++ b/EnumType.cpp
@@ -16,46 +16,123 @@
 
 #include "EnumType.h"
 
-#include "Annotation.h"
-#include "ScalarType.h"
-
-#include <inttypes.h>
 #include <hidl-util/Formatter.h>
-#include <android-base/logging.h>
+#include <inttypes.h>
+#include <iostream>
+#include <unordered_map>
+
+#include "Annotation.h"
+#include "Location.h"
+#include "ScalarType.h"
 
 namespace android {
 
-EnumType::EnumType(const char* localName, const Location& location, Type* storageType,
-                   Scope* parent)
-    : Scope(localName, location, parent), mValues(), mStorageType(storageType) {
-    mBitfieldType = new BitFieldType();
-    mBitfieldType->setElementType(this);
-}
+EnumType::EnumType(const char* localName, const FQName& fullName, const Location& location,
+                   const Reference<Type>& storageType, Scope* parent)
+    : Scope(localName, fullName, location, parent), mValues(), mStorageType(storageType) {}
 
 const Type *EnumType::storageType() const {
-    return mStorageType;
+    return mStorageType.get();
 }
 
 const std::vector<EnumValue *> &EnumType::values() const {
     return mValues;
 }
 
-void EnumType::addValue(EnumValue *value) {
-    CHECK(value != nullptr);
+void EnumType::forEachValueFromRoot(const std::function<void(EnumValue*)> f) const {
+    std::vector<const EnumType*> chain = typeChain();
+    for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
+        const auto& type = *it;
+        for (EnumValue* v : type->values()) {
+            f(v);
+        }
+    }
+}
 
-    EnumValue *prev = nullptr;
-    std::vector<const EnumType *> chain;
-    getTypeChain(&chain);
-    for (auto it = chain.begin(); it != chain.end(); ++it) {
-        const auto &type = *it;
-        if(!type->values().empty()) {
-            prev = type->values().back();
+void EnumType::addValue(EnumValue* value) {
+    CHECK(value != nullptr);
+    mValues.push_back(value);
+}
+
+status_t EnumType::resolveInheritance() {
+    const EnumType* prevType = nullptr;
+    EnumValue* prevValue = nullptr;
+
+    for (const auto* type : superTypeChain()) {
+        if (!type->values().empty()) {
+            prevType = type;
+            prevValue = type->values().back();
             break;
         }
     }
 
-    value->autofill(prev, resolveToScalarType());
-    mValues.push_back(value);
+    for (auto* value : mValues) {
+        value->autofill(prevType, prevValue, mStorageType->resolveToScalarType());
+        prevType = this;
+        prevValue = value;
+    }
+
+    return Scope::resolveInheritance();
+}
+
+std::vector<const Reference<Type>*> EnumType::getReferences() const {
+    return {&mStorageType};
+}
+
+std::vector<const ConstantExpression*> EnumType::getConstantExpressions() const {
+    std::vector<const ConstantExpression*> ret;
+    for (const auto* value : mValues) {
+        ret.push_back(value->constExpr());
+    }
+    return ret;
+}
+
+status_t EnumType::validate() const {
+    CHECK(getSubTypes().empty());
+
+    if (!isElidableType() || !mStorageType->isValidEnumStorageType()) {
+        std::cerr << "ERROR: Invalid enum storage type (" << (mStorageType)->typeName()
+                  << ") specified at " << mStorageType.location() << "\n";
+        return UNKNOWN_ERROR;
+    }
+
+    status_t err = validateUniqueNames();
+    if (err != OK) return err;
+
+    return Scope::validate();
+}
+
+status_t EnumType::validateUniqueNames() const {
+    std::unordered_map<std::string, const EnumType*> registeredValueNames;
+    for (const auto* type : superTypeChain()) {
+        for (const auto* enumValue : type->mValues) {
+            // No need to check super value uniqueness
+            registeredValueNames[enumValue->name()] = type;
+        }
+    }
+
+    for (const auto* value : mValues) {
+        auto registered = registeredValueNames.find(value->name());
+
+        if (registered != registeredValueNames.end()) {
+            const EnumType* definedInType = registered->second;
+
+            if (definedInType == this) {
+                // Defined in this enum
+                std::cerr << "ERROR: Redefinition of value '" << value->name() << "'";
+            } else {
+                // Defined in super enum
+                std::cerr << "ERROR: Redefinition of value '" << value->name()
+                          << "' defined in enum '" << definedInType->fullName() << "'";
+            }
+            std::cerr << " at " << value->location() << "\n";
+            return UNKNOWN_ERROR;
+        }
+
+        registeredValueNames[value->name()] = this;
+    }
+
+    return OK;
 }
 
 bool EnumType::isElidableType() const {
@@ -74,7 +151,7 @@
     return true;
 }
 
-bool EnumType::canCheckEquality() const {
+bool EnumType::deepCanCheckEquality(std::unordered_set<const Type*>* /* visited */) const {
     return true;
 }
 
@@ -99,13 +176,21 @@
     return "TYPE_ENUM";
 }
 
-BitFieldType *EnumType::getBitfieldType() const {
-    return mBitfieldType;
+std::string EnumType::getBitfieldCppType(StorageMode /* mode */, bool specifyNamespaces) const {
+    const std::string space = specifyNamespaces ? "::android::hardware::" : "";
+    return space + "hidl_bitfield<" + (specifyNamespaces ? fullName() : localName()) + ">";
+}
+
+std::string EnumType::getBitfieldJavaType(bool forInitializer) const {
+    return resolveToScalarType()->getJavaType(forInitializer);
+}
+
+std::string EnumType::getBitfieldJavaWrapperType() const {
+    return resolveToScalarType()->getJavaWrapperType();
 }
 
 LocalIdentifier *EnumType::lookupIdentifier(const std::string &name) const {
-    std::vector<const EnumType *> chain;
-    getTypeChain(&chain);
+    std::vector<const EnumType*> chain = typeChain();
     for (auto it = chain.begin(); it != chain.end(); ++it) {
         const auto &type = *it;
         for(EnumValue *v : type->values()) {
@@ -149,7 +234,7 @@
             out, depth, parcelName, blobName, fieldName, offset, isReader);
 }
 
-status_t EnumType::emitTypeDeclarations(Formatter &out) const {
+void EnumType::emitTypeDeclarations(Formatter& out) const {
     const ScalarType *scalarType = mStorageType->resolveToScalarType();
     CHECK(scalarType != nullptr);
 
@@ -163,13 +248,14 @@
 
     out.indent();
 
-    std::vector<const EnumType *> chain;
-    getTypeChain(&chain);
+    std::vector<const EnumType*> chain = typeChain();
 
     for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
         const auto &type = *it;
 
         for (const auto &entry : type->values()) {
+            entry->emitDocComment(out);
+
             out << entry->name();
 
             std::string value = entry->cppValue(scalarType->getKind());
@@ -179,7 +265,7 @@
             out << ",";
 
             std::string comment = entry->comment();
-            if (!comment.empty() && comment != value) {
+            if (!comment.empty()) {
                 out << " // " << comment;
             }
 
@@ -189,8 +275,42 @@
 
     out.unindent();
     out << "};\n\n";
+}
 
-    return OK;
+void EnumType::emitTypeForwardDeclaration(Formatter& out) const {
+    const ScalarType* scalarType = mStorageType->resolveToScalarType();
+    const std::string storageType = scalarType->getCppStackType();
+
+    out << "enum class " << localName() << " : " << storageType << ";\n";
+}
+
+void EnumType::emitIteratorDeclaration(Formatter& out) const {
+    size_t elementCount = 0;
+    for (const auto* type : typeChain()) {
+        elementCount += type->mValues.size();
+    }
+
+    out << "template<> struct hidl_enum_iterator<" << getCppStackType() << ">\n";
+    out.block([&] {
+        out << "const " << getCppStackType() << "* begin() { return static_begin(); }\n";
+        out << "const " << getCppStackType() << "* end() { return begin() + " << elementCount
+            << "; }\n";
+        out << "private:\n";
+        out << "static const " << getCppStackType() << "* static_begin() ";
+        out.block([&] {
+            out << "static const " << getCppStackType() << " kVals[" << elementCount << "] ";
+            out.block([&] {
+                auto enumerators = typeChain();
+                std::reverse(enumerators.begin(), enumerators.end());
+                for (const auto* type : enumerators) {
+                    for (const auto* enumValue : type->mValues) {
+                        out << fullName() << "::" << enumValue->name() << ",\n";
+                    }
+                }
+            }) << ";\n";
+            out << "return &kVals[0];\n";
+        });
+    }) << ";\n\n";
 }
 
 void EnumType::emitEnumBitwiseOperator(
@@ -258,7 +378,17 @@
     out << "}\n\n";
 }
 
-status_t EnumType::emitGlobalTypeDeclarations(Formatter &out) const {
+void EnumType::emitGlobalTypeDeclarations(Formatter& out) const {
+    out << "namespace android {\n";
+    out << "namespace hardware {\n";
+
+    emitIteratorDeclaration(out);
+
+    out << "}  // namespace hardware\n";
+    out << "}  // namespace android\n";
+}
+
+void EnumType::emitPackageTypeDeclarations(Formatter& out) const {
     emitEnumBitwiseOperator(out, true  /* lhsIsEnum */, true  /* rhsIsEnum */, "|");
     emitEnumBitwiseOperator(out, false /* lhsIsEnum */, true  /* rhsIsEnum */, "|");
     emitEnumBitwiseOperator(out, true  /* lhsIsEnum */, false /* rhsIsEnum */, "|");
@@ -269,40 +399,22 @@
     emitBitFieldBitwiseAssignmentOperator(out, "|");
     emitBitFieldBitwiseAssignmentOperator(out, "&");
 
-    // toString for bitfields, equivalent to dumpBitfield in Java
-    out << "template<typename>\n"
-        << "std::string toString("
-        << resolveToScalarType()->getCppArgumentType()
-        << " o);\n";
-    out << "template<>\n"
-        << "std::string toString<" << getCppStackType() << ">("
-        << resolveToScalarType()->getCppArgumentType()
-        << " o);\n\n";
-
-    // toString for enum itself
-    out << "std::string toString("
-        << getCppArgumentType()
-        << " o);\n\n";
-
-    return OK;
-}
-
-status_t EnumType::emitTypeDefinitions(Formatter &out, const std::string /* prefix */) const {
-
     const ScalarType *scalarType = mStorageType->resolveToScalarType();
     CHECK(scalarType != NULL);
 
+    out << "template<typename>\n"
+        << "static inline std::string toString(" << resolveToScalarType()->getCppArgumentType()
+        << " o);\n";
     out << "template<>\n"
-        << "std::string toString<" << getCppStackType() << ">("
-        << scalarType->getCppArgumentType()
-        << " o) ";
+        << "inline std::string toString<" << getCppStackType() << ">("
+        << scalarType->getCppArgumentType() << " o) ";
     out.block([&] {
         // include toHexString for scalar types
         out << "using ::android::hardware::details::toHexString;\n"
             << "std::string os;\n"
-            << getBitfieldType()->getCppStackType() << " flipped = 0;\n"
+            << getBitfieldCppType(StorageMode_Stack) << " flipped = 0;\n"
             << "bool first = true;\n";
-        for (EnumValue *value : values()) {
+        forEachValueFromRoot([&](EnumValue* value) {
             std::string valueName = fullName() + "::" + value->name();
             out.sIf("(o & " + valueName + ")" +
                     " == static_cast<" + scalarType->getCppStackType() +
@@ -312,7 +424,7 @@
                     << "first = false;\n"
                     << "flipped |= " << valueName << ";\n";
             }).endl();
-        }
+        });
         // put remaining bits
         out.sIf("o != flipped", [&] {
             out << "os += (first ? \"\" : \" | \");\n";
@@ -325,27 +437,23 @@
         out << "return os;\n";
     }).endl().endl();
 
-    out << "std::string toString("
-        << getCppArgumentType()
-        << " o) ";
+    out << "static inline std::string toString(" << getCppArgumentType() << " o) ";
 
     out.block([&] {
         out << "using ::android::hardware::details::toHexString;\n";
-        for (EnumValue *value : values()) {
+        forEachValueFromRoot([&](EnumValue* value) {
             out.sIf("o == " + fullName() + "::" + value->name(), [&] {
                 out << "return \"" << value->name() << "\";\n";
             }).endl();
-        }
+        });
         out << "std::string os;\n";
         scalarType->emitHexDump(out, "os",
             "static_cast<" + scalarType->getCppStackType() + ">(o)");
         out << "return os;\n";
     }).endl().endl();
-
-    return OK;
 }
 
-status_t EnumType::emitJavaTypeDeclarations(Formatter &out, bool atTopLevel) const {
+void EnumType::emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const {
     const ScalarType *scalarType = mStorageType->resolveToScalarType();
     CHECK(scalarType != NULL);
 
@@ -360,13 +468,14 @@
     const std::string typeName =
         scalarType->getJavaType(false /* forInitializer */);
 
-    std::vector<const EnumType *> chain;
-    getTypeChain(&chain);
+    std::vector<const EnumType*> chain = typeChain();
 
     for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
         const auto &type = *it;
 
         for (const auto &entry : type->values()) {
+            entry->emitDocComment(out);
+
             out << "public static final "
                 << typeName
                 << " "
@@ -381,7 +490,7 @@
             out << ";";
 
             std::string comment = entry->comment();
-            if (!comment.empty() && comment != value) {
+            if (!comment.empty()) {
                 out << " // " << comment;
             }
 
@@ -392,34 +501,34 @@
     out << "public static final String toString("
         << typeName << " o) ";
     out.block([&] {
-        for (EnumValue *value : values()) {
+        forEachValueFromRoot([&](EnumValue* value) {
             out.sIf("o == " + value->name(), [&] {
                 out << "return \"" << value->name() << "\";\n";
             }).endl();
-        }
+        });
         out << "return \"0x\" + ";
         scalarType->emitConvertToJavaHexString(out, "o");
         out << ";\n";
     }).endl();
 
-    auto bitfieldType = getBitfieldType()->getJavaType(false /* forInitializer */);
-    auto bitfieldWrapperType = getBitfieldType()->getJavaWrapperType();
+    auto bitfieldType = getBitfieldJavaType(false /* forInitializer */);
+    auto bitfieldWrapperType = getBitfieldJavaWrapperType();
     out << "\n"
         << "public static final String dumpBitfield("
         << bitfieldType << " o) ";
     out.block([&] {
         out << "java.util.ArrayList<String> list = new java.util.ArrayList<>();\n";
         out << bitfieldType << " flipped = 0;\n";
-        for (EnumValue *value : values()) {
+        forEachValueFromRoot([&](EnumValue* value) {
             if (value->constExpr()->castSizeT() == 0) {
                 out << "list.add(\"" << value->name() << "\"); // " << value->name() << " == 0\n";
-                continue;
+                return;  // continue to next value
             }
             out.sIf("(o & " + value->name() + ") == " + value->name(), [&] {
                 out << "list.add(\"" << value->name() << "\");\n";
                 out << "flipped |= " << value->name() << ";\n";
             }).endl();
-        }
+        });
         // put remaining bits
         out.sIf("o != flipped", [&] {
             out << "list.add(\"0x\" + ";
@@ -431,11 +540,9 @@
 
     out.unindent();
     out << "};\n\n";
-
-    return OK;
 }
 
-status_t EnumType::emitVtsTypeDeclarations(Formatter &out) const {
+void EnumType::emitVtsTypeDeclarations(Formatter& out) const {
     const ScalarType *scalarType = mStorageType->resolveToScalarType();
 
     out << "name: \"" << fullName() << "\"\n";
@@ -446,8 +553,7 @@
     out << "scalar_type: \""
         << scalarType->getVtsScalarType()
         << "\"\n\n";
-    std::vector<const EnumType *> chain;
-    getTypeChain(&chain);
+    std::vector<const EnumType*> chain = typeChain();
 
     for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
         const auto &type = *it;
@@ -470,13 +576,11 @@
 
     out.unindent();
     out << "}\n";
-    return OK;
 }
 
-status_t EnumType::emitVtsAttributeType(Formatter &out) const {
+void EnumType::emitVtsAttributeType(Formatter& out) const {
     out << "type: " << getVtsType() << "\n";
     out << "predefined_type: \"" << fullName() << "\"\n";
-    return OK;
 }
 
 void EnumType::emitJavaDump(
@@ -487,19 +591,28 @@
         << name << "));\n";
 }
 
-void EnumType::getTypeChain(std::vector<const EnumType *> *out) const {
-    out->clear();
-    const EnumType *type = this;
-    for (;;) {
-        out->push_back(type);
+std::vector<const EnumType*> EnumType::typeChain() const {
+    std::vector<const EnumType*> types;
+    for (const EnumType* type = this; type != nullptr;) {
+        types.push_back(type);
 
-        const Type *superType = type->storageType();
-        if (superType == NULL || !superType->isEnum()) {
-            break;
+        const Type* superType = type->storageType();
+        if (superType != nullptr && superType->isEnum()) {
+            type = static_cast<const EnumType*>(superType);
+        } else {
+            type = nullptr;
         }
-
-        type = static_cast<const EnumType *>(superType);
     }
+
+    return types;
+}
+
+std::vector<const EnumType*> EnumType::superTypeChain() const {
+    const Type* superType = storageType();
+    if (superType == nullptr || !superType->isEnum()) {
+        return {};
+    }
+    return static_cast<const EnumType*>(superType)->typeChain();
 }
 
 void EnumType::getAlignmentAndSize(size_t *align, size_t *size) const {
@@ -523,7 +636,7 @@
     }
 }
 
-status_t EnumType::emitExportedHeader(Formatter &out, bool forJava) const {
+void EnumType::emitExportedHeader(Formatter& out, bool forJava) const {
     const Annotation *annotation = findExportAnnotation();
     CHECK(annotation != nullptr);
 
@@ -557,7 +670,7 @@
 
     std::vector<const EnumType *> chain;
     if (exportParent) {
-        getTypeChain(&chain);
+        chain = typeChain();
     } else {
         chain = { this };
     }
@@ -596,7 +709,7 @@
                 out << ";";
 
                 std::string comment = entry->comment();
-                if (!comment.empty() && comment != value) {
+                if (!comment.empty()) {
                     out << " // " << comment;
                 }
 
@@ -610,7 +723,7 @@
         }
         out << "\n";
 
-        return OK;
+        return;
     }
 
     if (!name.empty()) {
@@ -634,7 +747,7 @@
             out << ",";
 
             std::string comment = entry->comment();
-            if (!comment.empty() && comment != value) {
+            if (!comment.empty()) {
                 out << " // " << comment;
             }
 
@@ -650,17 +763,12 @@
     }
 
     out << ";\n\n";
-
-    return OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-EnumValue::EnumValue(const char *name, ConstantExpression *value)
-    : mName(name),
-      mValue(value),
-      mIsAutoFill(false) {
-}
+EnumValue::EnumValue(const char* name, ConstantExpression* value, const Location& location)
+    : mName(name), mValue(value), mLocation(location), mIsAutoFill(false) {}
 
 std::string EnumValue::name() const {
     return mName;
@@ -682,6 +790,7 @@
 
 std::string EnumValue::comment() const {
     CHECK(mValue != nullptr);
+    if (mValue->descriptionIsTrivial()) return "";
     return mValue->description();
 }
 
@@ -690,18 +799,21 @@
     return mValue;
 }
 
-void EnumValue::autofill(const EnumValue *prev, const ScalarType *type) {
-    if(mValue != nullptr)
-        return;
+void EnumValue::autofill(const EnumType* prevType, EnumValue* prevValue, const ScalarType* type) {
+    // Value is defined explicitly
+    if (mValue != nullptr) return;
+
+    CHECK((prevType == nullptr) == (prevValue == nullptr));
+
     mIsAutoFill = true;
-    ConstantExpression *value = new ConstantExpression();
-    if(prev == nullptr) {
-        *value = ConstantExpression::Zero(type->getKind());
+    if (prevValue == nullptr) {
+        mValue = ConstantExpression::Zero(type->getKind()).release();
     } else {
-        CHECK(prev->mValue != nullptr);
-        *value = prev->mValue->addOne();
+        std::string description = prevType->fullName() + "." + prevValue->name() + " implicitly";
+        auto* prevReference = new ReferenceConstantExpression(
+            Reference<LocalIdentifier>(prevValue, mLocation), description);
+        mValue = prevReference->addOne(type->getKind()).release();
     }
-    mValue = value;
 }
 
 bool EnumValue::isAutoFill() const {
@@ -712,17 +824,28 @@
     return true;
 }
 
+const Location& EnumValue::location() const {
+    return mLocation;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
+BitFieldType::BitFieldType(Scope* parent) : TemplatedType(parent) {}
+
 bool BitFieldType::isBitField() const {
     return true;
 }
 
-std::string BitFieldType::typeName() const {
-    return "mask" + (mElementType == nullptr ? "" : (" of " + mElementType->typeName()));
+const EnumType* BitFieldType::getElementEnumType() const {
+    CHECK(mElementType.get() != nullptr && mElementType->isEnum());
+    return static_cast<const EnumType*>(mElementType.get());
 }
 
-bool BitFieldType::isCompatibleElementType(Type *elementType) const {
+std::string BitFieldType::templatedTypeName() const {
+    return "mask";
+}
+
+bool BitFieldType::isCompatibleElementType(const Type* elementType) const {
     return elementType->isEnum();
 }
 
@@ -732,11 +855,11 @@
 
 std::string BitFieldType::getCppType(StorageMode mode,
                                  bool specifyNamespaces) const {
-    return resolveToScalarType()->getCppType(mode, specifyNamespaces);
+    return getElementEnumType()->getBitfieldCppType(mode, specifyNamespaces);
 }
 
 std::string BitFieldType::getJavaType(bool forInitializer) const {
-    return resolveToScalarType()->getJavaType(forInitializer);
+    return getElementEnumType()->getBitfieldJavaType(forInitializer);
 }
 
 std::string BitFieldType::getJavaSuffix() const {
@@ -744,7 +867,7 @@
 }
 
 std::string BitFieldType::getJavaWrapperType() const {
-    return resolveToScalarType()->getJavaWrapperType();
+    return getElementEnumType()->getBitfieldJavaWrapperType();
 }
 
 std::string BitFieldType::getVtsType() const {
@@ -755,18 +878,17 @@
     return resolveToScalarType()->isElidableType();
 }
 
-bool BitFieldType::canCheckEquality() const {
-    return resolveToScalarType()->canCheckEquality();
+bool BitFieldType::deepCanCheckEquality(std::unordered_set<const Type*>* visited) const {
+    return resolveToScalarType()->canCheckEquality(visited);
 }
 
-status_t BitFieldType::emitVtsAttributeType(Formatter &out) const {
+void BitFieldType::emitVtsAttributeType(Formatter& out) const {
     out << "type: " << getVtsType() << "\n";
     out << "scalar_type: \""
         << mElementType->resolveToScalarType()->getVtsScalarType()
         << "\"\n";
-    out << "predefined_type: \""
-        << static_cast<NamedType *>(mElementType)->fullName() << "\"\n";
-    return OK;
+    out << "predefined_type: \"" << static_cast<const NamedType*>(mElementType.get())->fullName()
+        << "\"\n";
 }
 
 void BitFieldType::getAlignmentAndSize(size_t *align, size_t *size) const {
@@ -790,9 +912,9 @@
             true /* needsCast */);
 }
 
-EnumType *BitFieldType::getEnumType() const {
+const EnumType* BitFieldType::getEnumType() const {
     CHECK(mElementType->isEnum());
-    return static_cast<EnumType *>(mElementType);
+    return static_cast<const EnumType*>(mElementType.get());
 }
 
 // a bitfield maps to the underlying scalar type in C++, so operator<< is
diff --git a/EnumType.h b/EnumType.h
index d829292..eef4eb2 100644
--- a/EnumType.h
+++ b/EnumType.h
@@ -19,6 +19,7 @@
 #define ENUM_TYPE_H_
 
 #include "ConstantExpression.h"
+#include "Reference.h"
 #include "Scope.h"
 
 #include <vector>
@@ -29,12 +30,15 @@
 struct BitFieldType;
 
 struct EnumType : public Scope {
-    EnumType(const char* localName, const Location& location, Type* storageType, Scope* parent);
+    EnumType(const char* localName, const FQName& fullName, const Location& location,
+             const Reference<Type>& storageType, Scope* parent);
 
     const Type *storageType() const;
     const std::vector<EnumValue *> &values() const;
     void addValue(EnumValue *value);
 
+    void forEachValueFromRoot(const std::function<void(EnumValue*)> f) const;
+
     LocalIdentifier *lookupIdentifier(const std::string &name) const override;
 
     bool isElidableType() const override;
@@ -42,7 +46,7 @@
 
     std::string typeName() const override;
     bool isEnum() const override;
-    bool canCheckEquality() const override;
+    bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
 
     std::string getCppType(StorageMode mode,
                            bool specifyNamespaces) const override;
@@ -55,8 +59,19 @@
 
     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;
+
     // Return the type that corresponds to bitfield<T>.
-    BitFieldType *getBitfieldType() const;
+    const BitFieldType* getBitfieldType() const;
+
+    std::vector<const Reference<Type>*> getReferences() const override;
+    std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+    status_t resolveInheritance() override;
+    status_t validate() const override;
+    status_t validateUniqueNames() const;
 
     void emitReaderWriter(
             Formatter &out,
@@ -75,15 +90,15 @@
             const std::string &offset,
             bool isReader) const override;
 
-    status_t emitTypeDeclarations(Formatter &out) const override;
-    status_t emitGlobalTypeDeclarations(Formatter &out) const override;
-    status_t emitTypeDefinitions(Formatter &out, const std::string prefix) const override;
+    void emitTypeDeclarations(Formatter& out) const override;
+    void emitTypeForwardDeclaration(Formatter& out) const override;
+    void emitGlobalTypeDeclarations(Formatter& out) const override;
+    void emitPackageTypeDeclarations(Formatter& out) const override;
 
-    status_t emitJavaTypeDeclarations(
-            Formatter &out, bool atTopLevel) const override;
+    void emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const override;
 
-    status_t emitVtsTypeDeclarations(Formatter &out) const override;
-    status_t emitVtsAttributeType(Formatter &out) const override;
+    void emitVtsTypeDeclarations(Formatter& out) const override;
+    void emitVtsAttributeType(Formatter& out) const override;
 
     void emitJavaDump(
             Formatter &out,
@@ -95,12 +110,17 @@
     void appendToExportedTypesVector(
             std::vector<const Type *> *exportedTypes) const override;
 
-    status_t emitExportedHeader(Formatter &out, bool forJava) const override;
+    void emitExportedHeader(Formatter& out, bool forJava) const override;
 
-private:
-    void getTypeChain(std::vector<const EnumType *> *out) const;
+   private:
+    std::vector<const EnumType*> typeChain() const;
+    std::vector<const EnumType*> superTypeChain() const;
+
     const Annotation *findExportAnnotation() const;
 
+    void emitIteratorDeclaration(Formatter& out) const;
+    void emitIteratorDefinitions(Formatter& out) const;
+
     void emitEnumBitwiseOperator(
             Formatter &out,
             bool lhsIsEnum,
@@ -112,45 +132,50 @@
             const std::string &op) const;
 
     std::vector<EnumValue *> mValues;
-    Type *mStorageType;
-    BitFieldType *mBitfieldType;
+    Reference<Type> mStorageType;
 
     DISALLOW_COPY_AND_ASSIGN(EnumType);
 };
 
-struct EnumValue : public LocalIdentifier {
-    EnumValue(const char *name, ConstantExpression *value = nullptr);
+struct EnumValue : public LocalIdentifier, DocCommentable {
+    EnumValue(const char* name, ConstantExpression* value, const Location& location);
 
     std::string name() const;
     std::string value(ScalarType::Kind castKind) const;
     std::string cppValue(ScalarType::Kind castKind) const;
     std::string javaValue(ScalarType::Kind castKind) const;
     std::string comment() const;
-    void autofill(const EnumValue *prev, const ScalarType *type);
-    ConstantExpression *constExpr() const;
+    void autofill(const EnumType* prevType, EnumValue* prevValue, const ScalarType* type);
+    ConstantExpression* constExpr() const override;
 
     bool isAutoFill() const;
     bool isEnumValue() const override;
 
+    const Location& location() const;
 
+   private:
     std::string mName;
-    ConstantExpression *mValue;
+    ConstantExpression* mValue;
+    const Location mLocation;
     bool mIsAutoFill;
 
     DISALLOW_COPY_AND_ASSIGN(EnumValue);
 };
 
 struct BitFieldType : public TemplatedType {
+    BitFieldType(Scope* parent);
 
-    std::string typeName() const override;
+    std::string templatedTypeName() const override;
+
+    const EnumType* getElementEnumType() const;
 
     bool isBitField() const override;
 
-    bool isCompatibleElementType(Type *elementType) const override;
+    bool isCompatibleElementType(const Type* elementType) const override;
 
     bool isElidableType() const override;
 
-    bool canCheckEquality() const override;
+    bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
 
     const ScalarType *resolveToScalarType() const override;
 
@@ -165,9 +190,9 @@
 
     std::string getVtsType() const override;
 
-    EnumType *getEnumType() const;
+    const EnumType* getEnumType() const;
 
-    status_t emitVtsAttributeType(Formatter &out) const override;
+    void emitVtsAttributeType(Formatter& out) const override;
 
     void getAlignmentAndSize(size_t *align, size_t *size) const override;
 
diff --git a/FmqType.cpp b/FmqType.cpp
index ec32bd0..f5eaaa6 100644
--- a/FmqType.cpp
+++ b/FmqType.cpp
@@ -23,12 +23,11 @@
 
 namespace android {
 
-FmqType::FmqType(const char *nsp, const char *name)
-    : mNamespace(nsp), mName(name) {
-}
+FmqType::FmqType(const char* nsp, const char* name, Scope* parent)
+    : TemplatedType(parent), mNamespace(nsp), mName(name) {}
 
-std::string FmqType::typeName() const {
-    return mName + " of " + mElementType->typeName();
+std::string FmqType::templatedTypeName() const {
+    return mName;
 }
 
 std::string FmqType::fullName() const {
@@ -138,7 +137,7 @@
             mNamespace);
 }
 
-bool FmqType::isJavaCompatible() const {
+bool FmqType::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
     return false;
 }
 
@@ -160,7 +159,7 @@
     return true;
 }
 
-bool FmqType::isCompatibleElementType(Type *elementType) const {
+bool FmqType::isCompatibleElementType(const Type* elementType) const {
     return (!elementType->isInterface() && !elementType->needsEmbeddedReadWrite());
 }
 
@@ -169,9 +168,9 @@
         return "TYPE_FMQ_SYNC";
     } else if (mName == "MQDescriptorUnsync") {
         return "TYPE_FMQ_UNSYNC";
-    } else {
-        LOG(ERROR) << "Invalid fmq type name.\n";
     }
+
+    CHECK(false) << "Invalid FmqType.";
     return "";
 }
 
diff --git a/FmqType.h b/FmqType.h
index ef2b216..77ca150 100644
--- a/FmqType.h
+++ b/FmqType.h
@@ -23,11 +23,11 @@
 namespace android {
 
 struct FmqType : public TemplatedType {
-    FmqType(const char *nsp, const char *name);
+    FmqType(const char* nsp, const char* name, Scope* parent);
 
     std::string fullName() const;
 
-    std::string typeName() const;
+    std::string templatedTypeName() const;
 
     std::string getCppType(
             StorageMode mode,
@@ -54,13 +54,13 @@
             const std::string &parentName,
             const std::string &offsetText) const override;
 
-    bool isJavaCompatible() const override;
+    bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
 
     void getAlignmentAndSize(size_t *align, size_t *size) const override;
 
     bool needsEmbeddedReadWrite() const override;
     bool resultNeedsDeref() const override;
-    bool isCompatibleElementType(Type *elementType) const override;
+    bool isCompatibleElementType(const Type* elementType) const override;
 
     std::string getVtsType() const override;
     std::string getVtsValueName() const override;
diff --git a/HandleType.cpp b/HandleType.cpp
index 0e198f6..7b6545d 100644
--- a/HandleType.cpp
+++ b/HandleType.cpp
@@ -23,7 +23,7 @@
 
 namespace android {
 
-HandleType::HandleType() {}
+HandleType::HandleType(Scope* parent) : Type(parent) {}
 
 bool HandleType::isHandle() const {
     return true;
@@ -157,7 +157,7 @@
     return true;
 }
 
-bool HandleType::isJavaCompatible() const {
+bool HandleType::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
     return false;
 }
 
@@ -167,9 +167,8 @@
     *size = assertion.size();
 }
 
-status_t HandleType::emitVtsTypeDeclarations(Formatter &out) const {
+void HandleType::emitVtsTypeDeclarations(Formatter& out) const {
     out << "type: " << getVtsType() << "\n";
-    return OK;
 }
 
 }  // namespace android
diff --git a/HandleType.h b/HandleType.h
index ad1f681..feaddb0 100644
--- a/HandleType.h
+++ b/HandleType.h
@@ -23,7 +23,7 @@
 namespace android {
 
 struct HandleType : public Type {
-    HandleType();
+    HandleType(Scope* parent);
 
     bool isHandle() const override;
 
@@ -58,13 +58,13 @@
 
     bool needsEmbeddedReadWrite() const override;
 
-    bool isJavaCompatible() const override;
+    bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
 
     bool useNameInEmitReaderWriterEmbedded(bool isReader) const override;
 
     void getAlignmentAndSize(size_t *align, size_t *size) const override;
 
-    status_t emitVtsTypeDeclarations(Formatter &out) const override;
+    void emitVtsTypeDeclarations(Formatter& out) const override;
 };
 
 }  // namespace android
diff --git a/Hash.cpp b/Hash.cpp
index 032227f..7eb315b 100644
--- a/Hash.cpp
+++ b/Hash.cpp
@@ -16,6 +16,7 @@
 
 #include "Hash.h"
 
+#include <algorithm>
 #include <fstream>
 #include <iomanip>
 #include <map>
@@ -27,7 +28,9 @@
 
 namespace android {
 
-const Hash &Hash::getHash(const std::string &path) {
+const std::vector<uint8_t> Hash::kEmptyHash = std::vector<uint8_t>(SHA256_DIGEST_LENGTH, 0);
+
+Hash& Hash::getMutableHash(const std::string& path) {
     static std::map<std::string, Hash> hashes;
 
     auto it = hashes.find(path);
@@ -39,6 +42,14 @@
     return it->second;
 }
 
+const Hash& Hash::getHash(const std::string& path) {
+    return getMutableHash(path);
+}
+
+void Hash::clearHash(const std::string& path) {
+    getMutableHash(path).mHash = kEmptyHash;
+}
+
 static std::vector<uint8_t> sha256File(const std::string &path) {
     std::ifstream stream(path);
     std::stringstream fileStream;
@@ -156,16 +167,18 @@
     std::map<std::string,std::vector<std::string>> hashes;
 };
 
-std::vector<std::string> Hash::lookupHash(const std::string &path,
-                                          const std::string &interfaceName,
-                                          std::string *err) {
+std::vector<std::string> Hash::lookupHash(const std::string& path, const std::string& interfaceName,
+                                          std::string* err, bool* fileExists) {
     *err = "";
     const HashFile *file = HashFile::parse(path, err);
 
     if (file == nullptr || err->size() > 0) {
+        if (fileExists != nullptr) *fileExists = false;
         return {};
     }
 
+    if (fileExists != nullptr) *fileExists = true;
+
     return file->lookup(interfaceName);
 }
 
diff --git a/Interface.cpp b/Interface.cpp
index 243d103..1c66b31 100644
--- a/Interface.cpp
+++ b/Interface.cpp
@@ -28,10 +28,11 @@
 #include <unistd.h>
 
 #include <iostream>
+#include <memory>
 #include <sstream>
+#include <unordered_map>
 
 #include <android-base/logging.h>
-#include <hidl-hash/Hash.h>
 #include <hidl-util/Formatter.h>
 #include <hidl-util/StringHelper.h>
 
@@ -67,14 +68,18 @@
     LAST_HIDL_TRANSACTION   = 0x0fffffff,
 };
 
-Interface::Interface(const char* localName, const Location& location, Scope* parent,
-                     Interface* super)
-    : Scope(localName, location, parent), mSuperType(super), mIsJavaCompatibleInProgress(false) {}
+Interface::Interface(const char* localName, const FQName& fullName, const Location& location,
+                     Scope* parent, const Reference<Type>& superType, const Hash* fileHash)
+    : Scope(localName, fullName, location, parent), mSuperType(superType), mFileHash(fileHash) {}
 
 std::string Interface::typeName() const {
     return "interface " + localName();
 }
 
+const Hash* Interface::getFileHash() const {
+    return mFileHash;
+}
+
 bool Interface::fillPingMethod(Method *method) const {
     if (method->name() != "ping") {
         return false;
@@ -100,7 +105,6 @@
                     out << "return;\n";
                 }
             },
-            {IMPL_STUB, nullptr /* don't generate code */}
         } /*javaImpl*/
     );
 
@@ -211,7 +215,7 @@
                 out << "return ::android::hardware::Void();";
             } } }, /*cppImpl */
             { { IMPL_INTERFACE, [](auto &out) { /* javaImpl */
-                out << "android.os.SystemProperties.reportSyspropChanged();";
+                out << "android.os.HwBinder.enableInstrumentation();";
             } } } /*javaImpl */
     );
     return true;
@@ -284,22 +288,21 @@
     return true;
 }
 
-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 Hash &hash = Hash::getHash(iface->location().begin().filename());
+void Interface::emitDigestChain(
+    Formatter& out, const std::string& prefix, const std::vector<const Interface*>& chain,
+    std::function<std::string(std::unique_ptr<ConstantExpression>)> byteToString) const {
+    out.join(chain.begin(), chain.end(), ",\n", [&](const auto& iface) {
         out << prefix;
         out << "{";
-        out.join(hash.raw().begin(), hash.raw().end(), ",", [&](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.join(
+            iface->getFileHash()->raw().begin(), iface->getFileHash()->raw().end(), ",",
+            [&](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 << hash.hexString();
+        out << iface->getFileHash()->hexString();
         out << " */";
     });
 }
@@ -317,8 +320,8 @@
             std::vector<const Interface *> chain = typeChain();
             out << "_hidl_cb(";
             out.block([&] {
-                emitDigestChain(out, "(" + digestType->getInternalDataCppType() + ")",
-                    chain, [](const auto &e){return e.cppValue();});
+                emitDigestChain(out, "(" + digestType->getInternalDataCppType() + ")", chain,
+                                [](const auto& e) { return e->cppValue(); });
             });
             out << ");\n";
             out << "return ::android::hardware::Void();\n";
@@ -331,7 +334,7 @@
             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();});
+                                chain, [](const auto& e) { return e->javaValue(); });
             });
             out << "));\n";
         } } } /* javaImpl */
@@ -390,8 +393,7 @@
             {IMPL_STUB_IMPL,
                 [](auto &out) {
                     out << "::android::hidl::base::V1_0::DebugInfo info = {};\n";
-                    out << "info.pid = ::android::hardware::details::debuggable()"
-                        << "? getpid() : -1 /* pid */,\n";
+                    out << "info.pid = ::android::hardware::details::getPidIfSharable();\n";
                     out << "info.ptr = ::android::hardware::details::debuggable()"
                         << "? reinterpret_cast<uint64_t>(this) : 0;\n";
                     out << "info.arch = \n" << sArch << ";\n";
@@ -404,10 +406,9 @@
             const Type &refInfo = method->results().front()->type();
             out << refInfo.getJavaType(false /* forInitializer */) << " info = new "
                 << refInfo.getJavaType(true /* forInitializer */) << "();\n"
-                // TODO(b/34777099): PID for java.
-                << "info.pid = -1;\n"
+                << "info.pid = android.os.HidlSupport.getPidIfSharable();\n"
                 << "info.ptr = 0;\n"
-                << "info.arch = android.hidl.base.V1_0.DebugInfo.Architecture.UNKNOWN;"
+                << "info.arch = android.hidl.base.V1_0.DebugInfo.Architecture.UNKNOWN;\n"
                 << "return info;";
         } } } /* javaImpl */
     );
@@ -444,8 +445,8 @@
 bool Interface::addMethod(Method *method) {
     if (isIBase()) {
         if (!gAllReservedMethods.emplace(method->name(), method).second) {
-            LOG(ERROR) << "ERROR: hidl-gen encountered duplicated reserved method "
-                       << method->name();
+            std::cerr << "ERROR: hidl-gen encountered duplicated reserved method " << method->name()
+                      << std::endl;
             return false;
         }
         // will add it in addAllReservedMethods
@@ -453,28 +454,151 @@
     }
 
     CHECK(!method->isHidlReserved());
-    if (lookupMethod(method->name()) != nullptr) {
-        LOG(ERROR) << "Redefinition of method " << method->name();
-        return false;
-    }
-    size_t serial = FIRST_CALL_TRANSACTION;
-
-    serial += userDefinedMethods().size();
-
-    const Interface *ancestor = mSuperType;
-    while (ancestor != nullptr) {
-        serial += ancestor->userDefinedMethods().size();
-        ancestor = ancestor->superType();
-    }
-
-    CHECK(serial <= LAST_CALL_TRANSACTION) << "More than "
-            << LAST_CALL_TRANSACTION << " methods are not allowed.";
-    method->setSerialId(serial);
     mUserMethods.push_back(method);
 
     return true;
 }
 
+std::vector<const Reference<Type>*> Interface::getReferences() const {
+    std::vector<const Reference<Type>*> ret;
+
+    if (!isIBase()) {
+        ret.push_back(&mSuperType);
+    }
+
+    for (const auto* method : methods()) {
+        const auto& references = method->getReferences();
+        ret.insert(ret.end(), references.begin(), references.end());
+    }
+
+    return ret;
+}
+
+std::vector<const ConstantExpression*> Interface::getConstantExpressions() const {
+    std::vector<const ConstantExpression*> ret;
+    for (const auto* method : methods()) {
+        const auto& retMethod = method->getConstantExpressions();
+        ret.insert(ret.end(), retMethod.begin(), retMethod.end());
+    }
+    return ret;
+}
+
+std::vector<const Reference<Type>*> Interface::getStrongReferences() const {
+    // Interface is a special case as a reference:
+    // its definiton must be completed for extension but
+    // not necessary for other references.
+
+    std::vector<const Reference<Type>*> ret;
+    if (!isIBase()) {
+        ret.push_back(&mSuperType);
+    }
+
+    for (const auto* method : methods()) {
+        const auto& references = method->getStrongReferences();
+        ret.insert(ret.end(), references.begin(), references.end());
+    }
+
+    return ret;
+}
+
+status_t Interface::resolveInheritance() {
+    size_t serial = FIRST_CALL_TRANSACTION;
+    for (const auto* ancestor : superTypeChain()) {
+        serial += ancestor->mUserMethods.size();
+    }
+
+    for (Method* method : mUserMethods) {
+        if (serial > LAST_CALL_TRANSACTION) {
+            std::cerr << "ERROR: More than " << LAST_CALL_TRANSACTION
+                      << " methods (including super and reserved) are not allowed at " << location()
+                      << std::endl;
+            return UNKNOWN_ERROR;
+        }
+
+        method->setSerialId(serial);
+        serial++;
+    }
+
+    return Scope::resolveInheritance();
+}
+
+status_t Interface::validate() const {
+    CHECK(isIBase() == mSuperType.isEmptyReference());
+
+    if (!isIBase() && !mSuperType->isInterface()) {
+        std::cerr << "ERROR: You can only extend interfaces at " << mSuperType.location()
+                  << std::endl;
+        return UNKNOWN_ERROR;
+    }
+
+    status_t err;
+
+    err = validateUniqueNames();
+    if (err != OK) return err;
+
+    err = validateAnnotations();
+    if (err != OK) return err;
+
+    return Scope::validate();
+}
+
+void Interface::getAlignmentAndSize(size_t* align, size_t* size) const {
+    *align = 8;
+    *size = 8;
+}
+
+status_t Interface::validateUniqueNames() const {
+    std::unordered_map<std::string, const Interface*> registeredMethodNames;
+    for (auto const& tuple : allSuperMethodsFromRoot()) {
+        // No need to check super method uniqueness
+        registeredMethodNames[tuple.method()->name()] = tuple.interface();
+    }
+
+    for (const Method* method : mUserMethods) {
+        auto registered = registeredMethodNames.find(method->name());
+
+        if (registered != registeredMethodNames.end()) {
+            const Interface* definedInType = registered->second;
+
+            if (definedInType == this) {
+                // Defined in this interface
+                std::cerr << "ERROR: Redefinition of method '" << method->name() << "'";
+            } else if (definedInType->isIBase()) {
+                // Defined in IBase
+                std::cerr << "ERROR: Redefinition of reserved method '" << method->name() << "'";
+            } else {
+                // Defined in super not IBase
+                std::cerr << "ERROR: Redefinition of method '" << method->name()
+                          << "' defined in interface '" << definedInType->fullName() << "'";
+            }
+            std::cerr << " at " << method->location() << std::endl;
+            return UNKNOWN_ERROR;
+        }
+
+        registeredMethodNames[method->name()] = this;
+    }
+
+    return OK;
+}
+
+status_t Interface::validateAnnotations() const {
+    for (const Method* method : methods()) {
+        for (const Annotation* annotation : method->annotations()) {
+            const std::string name = annotation->name();
+
+            if (name == "entry" || name == "exit" || name == "callflow") {
+                continue;
+            }
+
+            std::cerr << "ERROR: Unrecognized annotation '" << name
+                      << "' for method: " << method->name() << ". An annotation should be one of: "
+                      << "entry, exit, callflow." << std::endl;
+            return UNKNOWN_ERROR;
+        }
+    }
+    return OK;
+}
+
 bool Interface::addAllReservedMethods() {
     // use a sorted map to insert them in serial ID order.
     std::map<int32_t, Method *> reservedMethodsById;
@@ -492,15 +616,14 @@
             || fillDebugMethod(method);
 
         if (!fillSuccess) {
-            LOG(ERROR) << "ERROR: hidl-gen does not recognize a reserved method "
-                       << method->name();
+            std::cerr << "ERROR: hidl-gen does not recognize a reserved method " << method->name()
+                      << std::endl;
             return false;
         }
         if (!reservedMethodsById.emplace(method->getSerialId(), method).second) {
-            LOG(ERROR) << "ERROR: hidl-gen uses duplicated serial id for "
-                       << method->name() << " and "
-                       << reservedMethodsById[method->getSerialId()]->name()
-                       << ", serialId = " << method->getSerialId();
+            std::cerr << "ERROR: hidl-gen uses duplicated serial id for " << method->name()
+                      << " and " << reservedMethodsById[method->getSerialId()]->name()
+                      << ", serialId = " << method->getSerialId() << std::endl;
             return false;
         }
     }
@@ -510,8 +633,14 @@
     return true;
 }
 
-const Interface *Interface::superType() const {
-    return mSuperType;
+const Interface* Interface::superType() const {
+    if (isIBase()) return nullptr;
+    if (!mSuperType->isInterface()) {
+        // This is actually an error
+        // that would be caught in validate
+        return nullptr;
+    }
+    return static_cast<const Interface*>(mSuperType.get());
 }
 
 std::vector<const Interface *> Interface::typeChain() const {
@@ -519,13 +648,13 @@
     const Interface *iface = this;
     while (iface != nullptr) {
         v.push_back(iface);
-        iface = iface->mSuperType;
+        iface = iface->superType();
     }
     return v;
 }
 
 std::vector<const Interface *> Interface::superTypeChain() const {
-    return superType()->typeChain(); // should work even if superType is nullptr
+    return isIBase() ? std::vector<const Interface*>() : superType()->typeChain();
 }
 
 bool Interface::isElidableType() const {
@@ -571,21 +700,18 @@
     return v;
 }
 
-Method *Interface::lookupMethod(std::string name) const {
-    for (const auto &tuple : allMethodsFromRoot()) {
-        Method *method = tuple.method();
-        if (method->name() == name) {
-            return method;
-        }
-    }
-
-    return nullptr;
+std::vector<InterfaceAndMethod> Interface::allSuperMethodsFromRoot() const {
+    return isIBase() ? std::vector<InterfaceAndMethod>() : superType()->allMethodsFromRoot();
 }
 
 std::string Interface::getBaseName() const {
     return fqName().getInterfaceBaseName();
 }
 
+std::string Interface::getAdapterName() const {
+    return fqName().getInterfaceAdapterName();
+}
+
 std::string Interface::getProxyName() const {
     return fqName().getInterfaceProxyName();
 }
@@ -658,8 +784,7 @@
         out << "{\n";
         out.indent();
 
-        const std::string binderName = "_hidl_" + name + "_binder";
-
+        const std::string binderName = "_hidl_binder";
         out << "::android::sp<::android::hardware::IBinder> "
             << binderName << ";\n";
 
@@ -720,29 +845,10 @@
     }
 }
 
-status_t Interface::emitGlobalTypeDeclarations(Formatter &out) const {
-    status_t status = Scope::emitGlobalTypeDeclarations(out);
-    if (status != OK) {
-        return status;
-    }
-    out << "std::string toString("
-        << getCppArgumentType()
-        << ");\n";
-    return OK;
-}
+void Interface::emitPackageTypeDeclarations(Formatter& out) const {
+    Scope::emitPackageTypeDeclarations(out);
 
-
-status_t Interface::emitTypeDefinitions(
-        Formatter &out, const std::string prefix) const {
-    std::string space = prefix.empty() ? "" : (prefix + "::");
-    status_t err = Scope::emitTypeDefinitions(out, space + localName());
-    if (err != OK) {
-        return err;
-    }
-
-    out << "std::string toString("
-        << getCppArgumentType()
-        << " o) ";
+    out << "static inline std::string toString(" << getCppArgumentType() << " o) ";
 
     out.block([&] {
         out << "std::string os = \"[class or subclass of \";\n"
@@ -751,8 +857,12 @@
             << "os += o->isRemote() ? \"@remote\" : \"@local\";\n"
             << "return os;\n";
     }).endl().endl();
+}
 
-    return OK;
+void Interface::emitTypeDefinitions(Formatter& out, const std::string& prefix) const {
+    std::string space = prefix.empty() ? "" : (prefix + "::");
+
+    Scope::emitTypeDefinitions(out, space + localName());
 }
 
 void Interface::emitJavaReaderWriter(
@@ -775,7 +885,7 @@
     }
 }
 
-status_t Interface::emitVtsAttributeDeclaration(Formatter &out) const {
+void Interface::emitVtsAttributeDeclaration(Formatter& out) const {
     for (const auto &type : getSubTypes()) {
         // Skip for TypeDef as it is just an alias of a defined type.
         if (type->isTypeDef()) {
@@ -783,17 +893,13 @@
         }
         out << "attribute: {\n";
         out.indent();
-        status_t status = type->emitVtsTypeDeclarations(out);
-        if (status != OK) {
-            return status;
-        }
+        type->emitVtsTypeDeclarations(out);
         out.unindent();
         out << "}\n\n";
     }
-    return OK;
 }
 
-status_t Interface::emitVtsMethodDeclaration(Formatter &out) const {
+void Interface::emitVtsMethodDeclaration(Formatter& out) const {
     for (const auto &method : methods()) {
         if (method->isHidlReserved()) {
             continue;
@@ -806,10 +912,7 @@
         for (const auto &result : method->results()) {
             out << "return_type_hidl: {\n";
             out.indent();
-            status_t status = result->type().emitVtsAttributeType(out);
-            if (status != OK) {
-                return status;
-            }
+            result->type().emitVtsAttributeType(out);
             out.unindent();
             out << "}\n";
         }
@@ -817,10 +920,7 @@
         for (const auto &arg : method->args()) {
             out << "arg: {\n";
             out.indent();
-            status_t status = arg->type().emitVtsAttributeType(out);
-            if (status != OK) {
-                return status;
-            }
+            arg->type().emitVtsAttributeType(out);
             out.unindent();
             out << "}\n";
         }
@@ -828,7 +928,7 @@
         for (const auto &annotation : method->annotations()) {
             out << "callflow: {\n";
             out.indent();
-            std::string name = annotation->name();
+            const std::string name = annotation->name();
             if (name == "entry") {
                 out << "entry: true\n";
             } else if (name == "exit") {
@@ -837,15 +937,12 @@
                 const AnnotationParam *param =
                         annotation->getParam("next");
                 if (param != nullptr) {
-                    for (auto value : *param->getValues()) {
+                    for (const auto& value : param->getValues()) {
                         out << "next: " << value << "\n";
                     }
                 }
             } else {
-                std::cerr << "Unrecognized annotation '"
-                          << name << "' for method: " << method->name()
-                          << ". A VTS annotation should be one of: "
-                          << "entry, exit, callflow. \n";
+                CHECK(false);
             }
             out.unindent();
             out << "}\n";
@@ -853,15 +950,13 @@
         out.unindent();
         out << "}\n\n";
     }
-    return OK;
 }
 
-status_t Interface::emitVtsAttributeType(Formatter &out) const {
+void Interface::emitVtsAttributeType(Formatter& out) const {
     out << "type: " << getVtsType() << "\n"
         << "predefined_type: \""
         << fullName()
         << "\"\n";
-    return OK;
 }
 
 bool Interface::hasOnewayMethods() const {
@@ -880,37 +975,21 @@
     return false;
 }
 
-bool Interface::isJavaCompatible() const {
-    if (mIsJavaCompatibleInProgress) {
-        // We're currently trying to determine if this Interface is
-        // java-compatible and something is referencing this interface through
-        // one of its methods. Assume we'll ultimately succeed, if we were wrong
-        // the original invocation of Interface::isJavaCompatible() will then
-        // return the correct "false" result.
-        return true;
-    }
-
-    if (mSuperType != nullptr && !mSuperType->isJavaCompatible()) {
-        mIsJavaCompatibleInProgress = false;
+bool Interface::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
+    if (superType() != nullptr && !superType()->isJavaCompatible(visited)) {
         return false;
     }
 
-    mIsJavaCompatibleInProgress = true;
-
-    if (!Scope::isJavaCompatible()) {
-        mIsJavaCompatibleInProgress = false;
-        return false;
-    }
-
-    for (const auto &method : methods()) {
-        if (!method->isJavaCompatible()) {
-            mIsJavaCompatibleInProgress = false;
+    for (const auto* method : methods()) {
+        if (!method->deepIsJavaCompatible(visited)) {
             return false;
         }
     }
 
-    mIsJavaCompatibleInProgress = false;
+    return Scope::isJavaCompatible(visited);
+}
 
+bool Interface::isNeverStrongReference() const {
     return true;
 }
 
diff --git a/Interface.h b/Interface.h
index b37dc60..0c94c0c 100644
--- a/Interface.h
+++ b/Interface.h
@@ -18,16 +18,28 @@
 
 #define INTERFACE_H_
 
-#include "Scope.h"
 #include <vector>
 
+#include <hidl-hash/Hash.h>
+
+#include "Reference.h"
+#include "Scope.h"
+
 namespace android {
 
 struct Method;
 struct InterfaceAndMethod;
 
 struct Interface : public Scope {
-    Interface(const char* localName, const Location& location, Scope* parent, Interface* super);
+    enum {
+        /////////////////// Flag(s) - DO NOT CHANGE
+        FLAG_ONEWAY = 0x00000001,
+    };
+
+    Interface(const char* localName, const FQName& fullName, const Location& location,
+              Scope* parent, const Reference<Type>& superType, const Hash* fileHash);
+
+    const Hash* getFileHash() const;
 
     bool addMethod(Method *method);
     bool addAllReservedMethods();
@@ -35,13 +47,11 @@
     bool isElidableType() const override;
     bool isInterface() const override;
     bool isBinder() const override;
-    bool isRootType() const { return mSuperType == nullptr; }
     bool isIBase() const { return fqName() == gIBaseFqName; }
     std::string typeName() const override;
 
-    const Interface *superType() const;
+    const Interface* superType() const;
 
-    Method *lookupMethod(std::string name) const;
     // Super type chain to root type.
     // First element is superType().
     std::vector<const Interface *> superTypeChain() const;
@@ -65,8 +75,12 @@
     // this->hidlReservedMethods()
     std::vector<InterfaceAndMethod> allMethodsFromRoot() const;
 
+    // allMethodsFromRoot for parent
+    std::vector<InterfaceAndMethod> allSuperMethodsFromRoot() const;
+
     // aliases for corresponding methods in this->fqName()
     std::string getBaseName() const;
+    std::string getAdapterName() const;
     std::string getProxyName() const;
     std::string getStubName() const;
     std::string getPassthroughName() const;
@@ -82,6 +96,16 @@
     std::string getJavaType(bool forInitializer) const override;
     std::string getVtsType() const override;
 
+    std::vector<const Reference<Type>*> getReferences() const override;
+    std::vector<const Reference<Type>*> getStrongReferences() const override;
+
+    std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+    status_t resolveInheritance() override;
+    status_t validate() const override;
+    status_t validateUniqueNames() const;
+    status_t validateAnnotations() const;
+
     void emitReaderWriter(
             Formatter &out,
             const std::string &name,
@@ -90,40 +114,49 @@
             bool isReader,
             ErrorMode mode) const override;
 
-    status_t emitGlobalTypeDeclarations(Formatter &out) const override;
-    status_t emitTypeDefinitions(
-            Formatter &out, const std::string prefix) const override;
+    void emitPackageTypeDeclarations(Formatter& out) const override;
+    void emitTypeDefinitions(Formatter& out, const std::string& prefix) const override;
 
+    void getAlignmentAndSize(size_t* align, size_t* size) const override;
     void emitJavaReaderWriter(
             Formatter &out,
             const std::string &parcelObj,
             const std::string &argName,
             bool isReader) const override;
 
-    status_t emitVtsAttributeType(Formatter &out) const override;
+    void emitVtsAttributeType(Formatter& out) const override;
 
-    status_t emitVtsAttributeDeclaration(Formatter &out) const;
-    status_t emitVtsMethodDeclaration(Formatter &out) const;
+    void emitVtsAttributeDeclaration(Formatter& out) const;
+    void emitVtsMethodDeclaration(Formatter& out) const;
 
     bool hasOnewayMethods() const;
 
-    bool isJavaCompatible() const override;
+    bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
 
-private:
-    Interface *mSuperType;
-    std::vector<Method *> mUserMethods;
-    std::vector<Method *> mReservedMethods;
-    mutable bool mIsJavaCompatibleInProgress;
-    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;
-    bool fillSetHALInstrumentationMethod(Method *method) const;
-    bool fillGetDebugInfoMethod(Method *method) const;
-    bool fillDebugMethod(Method *method) const;
+    bool isNeverStrongReference() const override;
+
+   private:
+    Reference<Type> mSuperType;
+
+    std::vector<Method*> mUserMethods;
+    std::vector<Method*> mReservedMethods;
+
+    const Hash* mFileHash;
+
+    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;
+    bool fillSetHALInstrumentationMethod(Method* method) const;
+    bool fillGetDebugInfoMethod(Method* method) const;
+    bool fillDebugMethod(Method* method) const;
+
+    void emitDigestChain(
+        Formatter& out, const std::string& prefix, const std::vector<const Interface*>& chain,
+        std::function<std::string(std::unique_ptr<ConstantExpression>)> byteToString) const;
 
     DISALLOW_COPY_AND_ASSIGN(Interface);
 };
@@ -135,7 +168,8 @@
           mMethod(method) {}
     Method *method() const { return mMethod; }
     const Interface *interface() const { return mInterface; }
-private:
+
+   private:
     // do not own these objects.
     const Interface *mInterface;
     Method *mMethod;
diff --git a/Location.cpp b/Location.cpp
new file mode 100644
index 0000000..2d834fb
--- /dev/null
+++ b/Location.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Location.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+
+Position::Position(std::string filename, size_t line, size_t column)
+    : mFilename(filename), mLine(line), mColumn(column) {}
+
+const std::string& Position::filename() const {
+    return mFilename;
+}
+
+size_t Position::line() const {
+    return mLine;
+}
+
+size_t Position::column() const {
+    return mColumn;
+}
+
+bool Position::inSameFile(const Position& lhs, const Position& rhs) {
+    return lhs.mFilename == rhs.mFilename;
+}
+
+bool Position::operator<(const Position& pos) const {
+    CHECK(inSameFile(*this, pos)) << "Cannot compare positions in different files";
+    if (mLine == pos.mLine) {
+        return mColumn < pos.mColumn;
+    }
+    return mLine < pos.mLine;
+}
+
+std::ostream& operator<<(std::ostream& ostr, const Position& pos) {
+    if (!pos.filename().empty()) {
+        ostr << pos.filename() << ":";
+    }
+    return ostr << pos.line() << "." << pos.column();
+}
+
+////////////////////////////////////////
+
+Location::Location(const Position& begin, const Position& end)
+    : mIsValid(true), mBegin(begin), mEnd(end) {}
+
+void Location::setLocation(const Position& begin, const Position& end) {
+    mIsValid = true;
+    mBegin = begin;
+    mEnd = end;
+}
+
+bool Location::isValid() const {
+    return mIsValid;
+}
+
+const Position& Location::begin() const {
+    return mBegin;
+}
+
+const Position& Location::end() const {
+    return mEnd;
+}
+
+Location Location::startOf(const std::string& path) {
+    return Location(Position(path, 1, 1), Position(path, 1, 1));
+}
+
+bool Location::inSameFile(const Location& lhs, const Location& rhs) {
+    return Position::inSameFile(lhs.mBegin, rhs.mBegin);
+}
+
+bool Location::intersect(const Location& lhs, const Location& rhs) {
+    if (!inSameFile(lhs, rhs)) return false;
+    return !(lhs.mEnd < rhs.mBegin || rhs.mEnd < lhs.mBegin);
+}
+
+bool Location::operator<(const Location& loc) const {
+    CHECK(inSameFile(*this, loc)) << "Cannot compare locations in different files";
+    CHECK(!intersect(*this, loc));
+    return mEnd < loc.mBegin;
+}
+
+std::ostream& operator<<(std::ostream& ostr, const Location& loc) {
+    Position last = Position(loc.end().filename(), loc.end().line(),
+                             std::max<size_t>(1u, loc.end().column() - 1));
+    ostr << loc.begin();
+    if (loc.begin().filename() != last.filename()) {
+        ostr << "-" << last;
+    } else if (loc.begin().line() != last.line()) {
+        ostr << "-" << last.line() << "." << last.column();
+    } else if (loc.begin().column() != last.column()) {
+        ostr << "-" << last.column();
+    }
+    return ostr;
+}
+
+}  // namespace android
diff --git a/Location.h b/Location.h
index 84dfc0e..2e99ab8 100644
--- a/Location.h
+++ b/Location.h
@@ -17,68 +17,67 @@
 #ifndef LOCATION_H_
 #define LOCATION_H_
 
-#include <iostream>
 #include <stdint.h>
+#include <ostream>
 #include <string>
 
 // Mimics for yy::location and yy::position
 namespace android {
 
 struct Position {
-    Position(std::string f, size_t l, size_t c)
-            : mFilename(f), mLine(l), mColumn(c) {}
-    inline const std::string &filename() const { return mFilename; }
-    inline size_t line() const { return mLine; }
-    inline size_t column() const { return mColumn; }
+    Position() = default;
+    Position(std::string filename, size_t line, size_t column);
 
-private:
+    const std::string& filename() const;
+
+    size_t line() const;
+    size_t column() const;
+
+    static bool inSameFile(const Position& lhs, const Position& rhs);
+
+    // Precondition: inSameFile()
+    bool operator<(const Position& pos) const;
+
+   private:
     // File name to which this position refers.
-    const std::string mFilename;
+    std::string mFilename;
     // Current line number.
-    const size_t mLine;
+    size_t mLine;
     // Current column number.
-    const size_t mColumn;
+    size_t mColumn;
 };
 
-inline std::ostream& operator<< (std::ostream& ostr, const Position& pos) {
-    if (!pos.filename().empty()) {
-        ostr << pos.filename() << ":";
-    }
-    return ostr << pos.line() << "." << pos.column();
-}
+std::ostream& operator<<(std::ostream& ostr, const Position& pos);
 
 struct Location {
-    Location (Position begin, Position end)
-            : mBegin(begin), mEnd(end) {}
-    inline const Position &begin() const { return mBegin; }
-    inline const Position &end() const { return mEnd; }
+    Location() = default;
+    Location(const Position& begin, const Position& end);
 
-    inline static Location startOf(const std::string &path) {
-        return Location(Position(path, 1, 1), Position(path, 1, 1));
-    }
+    void setLocation(const Position& begin, const Position& end);
 
-private:
+    bool isValid() const;
+    const Position& begin() const;
+    const Position& end() const;
+
+    static Location startOf(const std::string& path);
+
+    static bool inSameFile(const Location& lhs, const Location& rhs);
+    static bool intersect(const Location& lhs, const Location& rhs);
+
+    // Precondition: inSameFile() && !intersect()
+    bool operator<(const Location& loc) const;
+
+   private:
+    bool mIsValid = false;
+
     // Beginning of the located region.
-    const Position mBegin;
+    Position mBegin;
     // End of the located region.
-    const Position mEnd;
+    Position mEnd;
 };
 
-inline std::ostream& operator<< (std::ostream& ostr, const Location& loc) {
-    Position last = Position(loc.end().filename(),
-            loc.end().line(),
-            std::max<size_t>(1u, loc.end().column() - 1));
-    ostr << loc.begin();
-    if (loc.begin().filename() != last.filename()) {
-        ostr << "-" << last;
-    } else if (loc.begin().line() != last.line()) {
-        ostr << "-" << last.line()  << "." << last.column();
-    } else if (loc.begin().column() != last.column()) {
-        ostr << "-" << last.column();
-    }
-    return ostr;
-}
+std::ostream& operator<<(std::ostream& ostr, const Location& loc);
 
 } // namespace android
 
-#endif
+#endif  // LOCATION_H_
diff --git a/MemoryType.cpp b/MemoryType.cpp
index 251691e..444ba92 100644
--- a/MemoryType.cpp
+++ b/MemoryType.cpp
@@ -23,7 +23,7 @@
 
 namespace android {
 
-MemoryType::MemoryType() {}
+MemoryType::MemoryType(Scope* parent) : Type(parent) {}
 
 std::string MemoryType::getCppType(StorageMode mode,
                                    bool specifyNamespaces) const {
@@ -145,7 +145,7 @@
     return true;
 }
 
-bool MemoryType::isJavaCompatible() const {
+bool MemoryType::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
     return false;
 }
 
@@ -155,9 +155,8 @@
     *size = assertion.size();
 }
 
-status_t MemoryType::emitVtsTypeDeclarations(Formatter &out) const {
+void MemoryType::emitVtsTypeDeclarations(Formatter& out) const {
     out << "type: " << getVtsType() << "\n";
-    return OK;
 }
 
 }  // namespace android
diff --git a/MemoryType.h b/MemoryType.h
index cd7329a..7bce55f 100644
--- a/MemoryType.h
+++ b/MemoryType.h
@@ -23,7 +23,7 @@
 namespace android {
 
 struct MemoryType : public Type {
-    MemoryType();
+    MemoryType(Scope* parent);
 
     std::string typeName() const override;
 
@@ -58,11 +58,12 @@
     bool resultNeedsDeref() const override;
 
     bool isMemory() const override;
-    bool isJavaCompatible() const override;
+
+    bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
 
     void getAlignmentAndSize(size_t *align, size_t *size) const override;
 
-    status_t emitVtsTypeDeclarations(Formatter &out) const override;
+    void emitVtsTypeDeclarations(Formatter& out) const override;
 };
 
 }  // namespace android
diff --git a/Method.cpp b/Method.cpp
index 6c0ebf5..64a9a65 100644
--- a/Method.cpp
+++ b/Method.cpp
@@ -17,25 +17,25 @@
 #include "Method.h"
 
 #include "Annotation.h"
+#include "ConstantExpression.h"
 #include "ScalarType.h"
 #include "Type.h"
 
 #include <android-base/logging.h>
 #include <hidl-util/Formatter.h>
+#include <algorithm>
 
 namespace android {
 
-Method::Method(const char *name,
-       std::vector<TypedVar *> *args,
-       std::vector<TypedVar *> *results,
-       bool oneway,
-       std::vector<Annotation *> *annotations)
+Method::Method(const char* name, std::vector<NamedReference<Type>*>* args,
+               std::vector<NamedReference<Type>*>* results, bool oneway,
+               std::vector<Annotation*>* annotations, const Location& location)
     : mName(name),
       mArgs(args),
       mResults(results),
       mOneway(oneway),
-      mAnnotations(annotations) {
-}
+      mAnnotations(annotations),
+      mLocation(location) {}
 
 void Method::fillImplementation(
         size_t serial,
@@ -57,11 +57,11 @@
     return mName;
 }
 
-const std::vector<TypedVar *> &Method::args() const {
+const std::vector<NamedReference<Type>*>& Method::args() const {
     return *mArgs;
 }
 
-const std::vector<TypedVar *> &Method::results() const {
+const std::vector<NamedReference<Type>*>& Method::results() const {
     return *mResults;
 }
 
@@ -69,6 +69,56 @@
     return *mAnnotations;
 }
 
+std::vector<Reference<Type>*> Method::getReferences() {
+    const auto& constRet = static_cast<const Method*>(this)->getReferences();
+    std::vector<Reference<Type>*> ret(constRet.size());
+    std::transform(constRet.begin(), constRet.end(), ret.begin(),
+                   [](const auto* ref) { return const_cast<Reference<Type>*>(ref); });
+    return ret;
+}
+
+std::vector<const Reference<Type>*> Method::getReferences() const {
+    std::vector<const Reference<Type>*> ret;
+    ret.insert(ret.end(), mArgs->begin(), mArgs->end());
+    ret.insert(ret.end(), mResults->begin(), mResults->end());
+    return ret;
+}
+
+std::vector<Reference<Type>*> Method::getStrongReferences() {
+    const auto& constRet = static_cast<const Method*>(this)->getStrongReferences();
+    std::vector<Reference<Type>*> ret(constRet.size());
+    std::transform(constRet.begin(), constRet.end(), ret.begin(),
+                   [](const auto* ref) { return const_cast<Reference<Type>*>(ref); });
+    return ret;
+}
+
+std::vector<const Reference<Type>*> Method::getStrongReferences() const {
+    std::vector<const Reference<Type>*> ret;
+    for (const auto* ref : getReferences()) {
+        if (!ref->shallowGet()->isNeverStrongReference()) {
+            ret.push_back(ref);
+        }
+    }
+    return ret;
+}
+
+std::vector<ConstantExpression*> Method::getConstantExpressions() {
+    const auto& constRet = static_cast<const Method*>(this)->getConstantExpressions();
+    std::vector<ConstantExpression*> ret(constRet.size());
+    std::transform(constRet.begin(), constRet.end(), ret.begin(),
+                   [](const auto* ce) { return const_cast<ConstantExpression*>(ce); });
+    return ret;
+}
+
+std::vector<const ConstantExpression*> Method::getConstantExpressions() const {
+    std::vector<const ConstantExpression*> ret;
+    for (const auto* annotation : *mAnnotations) {
+        const auto& retAnnotation = annotation->getConstantExpressions();
+        ret.insert(ret.end(), retAnnotation.begin(), retAnnotation.end());
+    }
+    return ret;
+}
+
 void Method::cppImpl(MethodImplType type, Formatter &out) const {
     CHECK(mIsHidlReserved);
     auto it = mCppImpl.find(type);
@@ -104,7 +154,7 @@
 }
 
 Method *Method::copySignature() const {
-    return new Method(mName.c_str(), mArgs, mResults, mOneway, mAnnotations);
+    return new Method(mName.c_str(), mArgs, mResults, mOneway, mAnnotations, Location());
 }
 
 void Method::setSerialId(size_t serial) {
@@ -121,7 +171,7 @@
 }
 
 void Method::generateCppReturnType(Formatter &out, bool specifyNamespaces) const {
-    const TypedVar *elidedReturn = canElideCallback();
+    const NamedReference<Type>* elidedReturn = canElideCallback();
     const std::string space = (specifyNamespaces ? "::android::hardware::" : "");
 
     if (elidedReturn == nullptr) {
@@ -149,9 +199,9 @@
     out << ")";
 }
 
-static void emitCppArgResultSignature(Formatter &out,
-                         const std::vector<TypedVar *> &args,
-                         bool specifyNamespaces) {
+static void emitCppArgResultSignature(Formatter& out,
+                                      const std::vector<NamedReference<Type>*>& args,
+                                      bool specifyNamespaces) {
     out.join(args.begin(), args.end(), ", ", [&](auto arg) {
         out << arg->type().getCppArgumentType(specifyNamespaces);
         out << " ";
@@ -159,7 +209,8 @@
     });
 }
 
-static void emitJavaArgResultSignature(Formatter &out, const std::vector<TypedVar *> &args) {
+static void emitJavaArgResultSignature(Formatter& out,
+                                       const std::vector<NamedReference<Type>*>& args) {
     out.join(args.begin(), args.end(), ", ", [&](auto arg) {
         out << arg->type().getJavaType();
         out << " ";
@@ -171,7 +222,7 @@
     emitCppArgResultSignature(out, args(), specifyNamespaces);
 
     const bool returnsValue = !results().empty();
-    const TypedVar *elidedReturn = canElideCallback();
+    const NamedReference<Type>* elidedReturn = canElideCallback();
     if (returnsValue && elidedReturn == nullptr) {
         if (!args().empty()) {
             out << ", ";
@@ -205,33 +256,31 @@
     out << "\n";
 }
 
-bool Method::isJavaCompatible() const {
+bool Method::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
     if (isHiddenFromJava()) {
         return true;
     }
 
-    for (const auto &arg : *mArgs) {
-        if (!arg->isJavaCompatible()) {
-            return false;
-        }
+    if (!std::all_of(mArgs->begin(), mArgs->end(),
+                     [&](const auto* arg) { return (*arg)->isJavaCompatible(visited); })) {
+        return false;
     }
 
-    for (const auto &result : *mResults) {
-        if (!result->isJavaCompatible()) {
-            return false;
-        }
+    if (!std::all_of(mResults->begin(), mResults->end(),
+                     [&](const auto* arg) { return (*arg)->isJavaCompatible(visited); })) {
+        return false;
     }
 
     return true;
 }
 
-const TypedVar* Method::canElideCallback() const {
+const NamedReference<Type>* Method::canElideCallback() const {
     // Can't elide callback for void or tuple-returning methods
     if (mResults->size() != 1) {
         return nullptr;
     }
 
-    const TypedVar *typedVar = mResults->at(0);
+    const NamedReference<Type>* typedVar = mResults->at(0);
 
     if (typedVar->type().isElidableType()) {
         return typedVar;
@@ -240,27 +289,13 @@
     return nullptr;
 }
 
-////////////////////////////////////////////////////////////////////////////////
-
-TypedVar::TypedVar(const char *name, Type *type)
-    : mName(name),
-      mType(type) {
-}
-
-std::string TypedVar::name() const {
-    return mName;
-}
-
-const Type &TypedVar::type() const {
-    return *mType;
-}
-
-bool TypedVar::isJavaCompatible() const {
-    return mType->isJavaCompatible();
+const Location& Method::location() const {
+    return mLocation;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-bool TypedVarVector::add(TypedVar *v) {
+
+bool TypedVarVector::add(NamedReference<Type>* v) {
     if (mNames.emplace(v->name()).second) {
         push_back(v);
         return true;
diff --git a/Method.h b/Method.h
index 6d7664b..711f087 100644
--- a/Method.h
+++ b/Method.h
@@ -19,20 +19,26 @@
 #define METHOD_H_
 
 #include <android-base/macros.h>
-#include <functional>
 #include <hidl-util/Formatter.h>
+#include <utils/Errors.h>
+#include <functional>
 #include <map>
 #include <set>
 #include <string>
+#include <unordered_set>
 #include <vector>
 
+#include "DocComment.h"
+#include "Location.h"
+#include "Reference.h"
+
 namespace android {
 
 struct Annotation;
+struct ConstantExpression;
 struct Formatter;
 struct ScalarType;
 struct Type;
-struct TypedVar;
 struct TypedVarVector;
 
 enum MethodImplType {
@@ -45,16 +51,14 @@
 
 using MethodImpl = std::map<MethodImplType, std::function<void(Formatter &)>>;
 
-struct Method {
-    Method(const char *name,
-           std::vector<TypedVar *> *args,
-           std::vector<TypedVar *> *results,
-           bool oneway,
-           std::vector<Annotation *> *annotations);
+struct Method : DocCommentable {
+    Method(const char* name, std::vector<NamedReference<Type>*>* args,
+           std::vector<NamedReference<Type>*>* results, bool oneway,
+           std::vector<Annotation*>* annotations, const Location& location);
 
     std::string name() const;
-    const std::vector<TypedVar *> &args() const;
-    const std::vector<TypedVar *> &results() const;
+    const std::vector<NamedReference<Type>*>& args() const;
+    const std::vector<NamedReference<Type>*>& results() const;
     bool isOneway() const { return mOneway; }
     bool overridesCppImpl(MethodImplType type) const;
     bool overridesJavaImpl(MethodImplType type) const;
@@ -64,6 +68,15 @@
     bool isHiddenFromJava() const;
     const std::vector<Annotation *> &annotations() const;
 
+    std::vector<Reference<Type>*> getReferences();
+    std::vector<const Reference<Type>*> getReferences() const;
+
+    std::vector<Reference<Type>*> getStrongReferences();
+    std::vector<const Reference<Type>*> getStrongReferences() const;
+
+    std::vector<ConstantExpression*> getConstantExpressions();
+    std::vector<const ConstantExpression*> getConstantExpressions() const;
+
     // Make a copy with the same name, args, results, oneway, annotations.
     // Implementations, serial are not copied.
     Method *copySignature() const;
@@ -90,17 +103,19 @@
     void emitJavaArgSignature(Formatter &out) const;
     void emitJavaResultSignature(Formatter &out) const;
 
-    const TypedVar* canElideCallback() const;
+    const NamedReference<Type>* canElideCallback() const;
 
     void dumpAnnotations(Formatter &out) const;
 
-    bool isJavaCompatible() const;
+    bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const;
 
-private:
+    const Location& location() const;
+
+   private:
     std::string mName;
     size_t mSerial = 0;
-    std::vector<TypedVar *> *mArgs;
-    std::vector<TypedVar *> *mResults;
+    std::vector<NamedReference<Type>*>* mArgs;
+    std::vector<NamedReference<Type>*>* mResults;
     bool mOneway;
     std::vector<Annotation *> *mAnnotations;
 
@@ -110,29 +125,17 @@
     MethodImpl mCppImpl;
     MethodImpl mJavaImpl;
 
+    const Location mLocation;
+
     DISALLOW_COPY_AND_ASSIGN(Method);
 };
 
-struct TypedVar {
-    TypedVar(const char *name, Type *type);
-
-    std::string name() const;
-    const Type &type() const;
-
-    bool isJavaCompatible() const;
-
-private:
-    std::string mName;
-    Type *mType;
-
-    DISALLOW_COPY_AND_ASSIGN(TypedVar);
-};
-
-struct TypedVarVector : public std::vector<TypedVar *> {
+struct TypedVarVector : public std::vector<NamedReference<Type>*> {
     TypedVarVector() = default;
 
-    bool add(TypedVar *v);
-private:
+    bool add(NamedReference<Type>* v);
+
+   private:
     std::set<std::string> mNames;
 };
 
diff --git a/NamedType.cpp b/NamedType.cpp
index f6d876f..645c63d 100644
--- a/NamedType.cpp
+++ b/NamedType.cpp
@@ -18,17 +18,14 @@
 
 namespace android {
 
-NamedType::NamedType(const char* localName, const Location& loc, Scope* parent)
-    : mLocalName(localName), mLocation(loc), mParent(parent) {}
+NamedType::NamedType(const char* localName, const FQName& fullName, const Location& loc,
+                     Scope* parent)
+    : Type(parent), mLocalName(localName), mFullName(fullName), mLocation(loc) {}
 
 bool NamedType::isNamedType() const {
     return true;
 }
 
-void NamedType::setFullName(const FQName &fullName) {
-    mFullName = fullName;
-}
-
 const FQName &NamedType::fqName() const {
     return mFullName;
 }
@@ -41,10 +38,6 @@
     return mFullName.cppName();
 }
 
-std::string NamedType::partialCppName() const {
-    return mFullName.cppLocalName();
-}
-
 std::string NamedType::fullJavaName() const {
     return mFullName.javaName();
 }
@@ -53,10 +46,6 @@
     return mLocation;
 }
 
-Scope* NamedType::parent() const {
-    return mParent;
-}
-
 void NamedType::emitDump(
         Formatter &out,
         const std::string &streamName,
diff --git a/NamedType.h b/NamedType.h
index cfc3df8..857f3a3 100644
--- a/NamedType.h
+++ b/NamedType.h
@@ -27,41 +27,31 @@
 
 namespace android {
 
-struct Scope;
-
 struct NamedType : public Type {
-    NamedType(const char* localName, const Location& loc, Scope* parent);
+    NamedType(const char* localName, const FQName& fullName, const Location& loc, Scope* parent);
 
     bool isNamedType() const override;
 
-    void setFullName(const FQName &fullName);
-
     const FQName &fqName() const;
 
     std::string localName() const;
 
     /* short for fqName().cppName() */
     std::string fullName() const;
-    /* short for fqName().cppLocalName() */
-    std::string partialCppName() const;
     /* short for fqName().fullJavaName() */
     std::string fullJavaName() const;
 
-    // returns null if no location is set for this type.
-    const Location &location() const;
+    const Location& location() const;
 
     void emitDump(
             Formatter &out,
             const std::string &streamName,
             const std::string &name) const override;
 
-    Scope* parent() const;
-
    private:
-    std::string mLocalName;
-    FQName mFullName;
-    Location mLocation;
-    Scope* const mParent;
+    const std::string mLocalName;
+    const FQName mFullName;
+    const Location mLocation;
 
     DISALLOW_COPY_AND_ASSIGN(NamedType);
 };
diff --git a/PointerType.cpp b/PointerType.cpp
index 8684e4e..cdf4a99 100644
--- a/PointerType.cpp
+++ b/PointerType.cpp
@@ -21,7 +21,7 @@
 
 namespace android {
 
-PointerType::PointerType() {}
+PointerType::PointerType(Scope* parent) : Type(parent) {}
 
 bool PointerType::isPointer() const {
     return true;
@@ -63,17 +63,16 @@
     return false;
 }
 
-bool PointerType::isJavaCompatible() const {
+bool PointerType::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
     return false;
 }
 
-bool PointerType::containsPointer() const {
+bool PointerType::deepContainsPointer(std::unordered_set<const Type*>* /* visited */) const {
     return true;
 }
 
-status_t PointerType::emitVtsTypeDeclarations(Formatter &out) const {
+void PointerType::emitVtsTypeDeclarations(Formatter& out) const {
     out << "type: " << getVtsType() << "\n";
-    return OK;
 }
 
 }  // namespace android
diff --git a/PointerType.h b/PointerType.h
index 4fb6a2f..30c4a32 100644
--- a/PointerType.h
+++ b/PointerType.h
@@ -23,7 +23,7 @@
 namespace android {
 
 struct PointerType : public Type {
-    PointerType();
+    PointerType(Scope* parent);
 
     bool isPointer() const override;
 
@@ -48,10 +48,10 @@
     bool needsEmbeddedReadWrite() const override;
     bool resultNeedsDeref() const override;
 
-    bool isJavaCompatible() const override;
-    bool containsPointer() const override;
+    bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
+    bool deepContainsPointer(std::unordered_set<const Type*>* visited) const override;
 
-    status_t emitVtsTypeDeclarations(Formatter &out) const override;
+    void emitVtsTypeDeclarations(Formatter& out) const override;
 };
 
 }  // namespace android
diff --git a/RefType.cpp b/RefType.cpp
index b43c832..9947399 100644
--- a/RefType.cpp
+++ b/RefType.cpp
@@ -24,11 +24,14 @@
 
 namespace android {
 
-RefType::RefType() {
+RefType::RefType(Scope* parent) : TemplatedType(parent) {}
+
+std::string RefType::templatedTypeName() const {
+    return "ref";
 }
 
-std::string RefType::typeName() const {
-    return "ref" + (mElementType == nullptr ? "" : (" of " + mElementType->typeName()));
+std::vector<const Reference<Type>*> RefType::getStrongReferences() const {
+    return {};
 }
 
 std::string RefType::getVtsType() const {
@@ -39,7 +42,7 @@
     return "ref_value";
 }
 
-bool RefType::isCompatibleElementType(Type *elementType) const {
+bool RefType::isCompatibleElementType(const Type* elementType) const {
     if (elementType->isScalar()) {
         return true;
     }
@@ -52,15 +55,17 @@
     if (elementType->isBitField()) {
         return true;
     }
-    if (elementType->isCompoundType()
-            && static_cast<CompoundType *>(elementType)->style() == CompoundType::STYLE_STRUCT) {
+    if (elementType->isCompoundType() &&
+        static_cast<const CompoundType*>(elementType)->style() == CompoundType::STYLE_STRUCT) {
         return true;
     }
     if (elementType->isTemplatedType()) {
-        return this->isCompatibleElementType(static_cast<TemplatedType *>(elementType)->getElementType());
+        return this->isCompatibleElementType(
+            static_cast<const TemplatedType*>(elementType)->getElementType());
     }
     if (elementType->isArray()) {
-        return this->isCompatibleElementType(static_cast<ArrayType *>(elementType)->getElementType());
+        return this->isCompatibleElementType(
+            static_cast<const ArrayType*>(elementType)->getElementType());
     }
     return false;
 }
@@ -225,7 +230,7 @@
     out << "}\n\n";
 }
 
-bool RefType::needsResolveReferences() const {
+bool RefType::deepNeedsResolveReferences(std::unordered_set<const Type*>* /* visited */) const {
     return true;
 }
 
@@ -237,11 +242,11 @@
     return false;
 }
 
-bool RefType::isJavaCompatible() const {
+bool RefType::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
     return false;
 }
 
-bool RefType::containsPointer() const {
+bool RefType::deepContainsPointer(std::unordered_set<const Type*>* /* visited */) const {
     return true;
 }
 
diff --git a/RefType.h b/RefType.h
index c44b8ad..d7b428c 100644
--- a/RefType.h
+++ b/RefType.h
@@ -18,15 +18,21 @@
 
 #define REF_TYPE_H_
 
+#include <vector>
+
+#include "Reference.h"
 #include "Type.h"
 
 namespace android {
 
 struct RefType : public TemplatedType {
-    RefType();
+    RefType(Scope* parent);
 
-    std::string typeName() const override;
-    bool isCompatibleElementType(Type *elementType) const override;
+    std::string templatedTypeName() const override;
+
+    bool isCompatibleElementType(const Type* elementType) const override;
+
+    std::vector<const Reference<Type>*> getStrongReferences() const override;
 
     std::string getCppType(StorageMode mode,
                            bool specifyNamespaces) const override;
@@ -65,13 +71,13 @@
             const std::string &offsetText) const override;
 
     bool needsEmbeddedReadWrite() const override;
-    bool needsResolveReferences() const override;
+    bool deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const override;
     bool resultNeedsDeref() const override;
 
-    bool isJavaCompatible() const override;
-    bool containsPointer() const override;
+    bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
+    bool deepContainsPointer(std::unordered_set<const Type*>* visited) const override;
 
- private:
+   private:
     DISALLOW_COPY_AND_ASSIGN(RefType);
 };
 
diff --git a/Reference.h b/Reference.h
new file mode 100644
index 0000000..7ddc889
--- /dev/null
+++ b/Reference.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef REFERENCE_H_
+
+#define REFERENCE_H_
+
+#include <android-base/logging.h>
+#include <hidl-util/FQName.h>
+
+#include "DocComment.h"
+#include "Location.h"
+
+namespace android {
+
+/**
+ * Reference placeholder
+ */
+template <class T>
+struct Reference {
+    Reference() = default;
+    virtual ~Reference() {}
+
+    Reference(const FQName& fqName, const Location& location)
+        : mResolved(nullptr), mFqName(fqName), mLocation(location) {}
+
+    Reference(T* type, const Location& location) : mResolved(type), mLocation(location) {
+        CHECK(type != nullptr);
+    }
+
+    template <class OtherT>
+    Reference(const Reference<OtherT>& ref)
+        : mResolved(ref.mResolved), mFqName(ref.mFqName), mLocation(ref.mLocation) {}
+
+    template <class OtherT>
+    Reference(const Reference<OtherT>& ref, const Location& location)
+        : mResolved(ref.mResolved), mFqName(ref.mFqName), mLocation(location) {}
+
+    /* Returns true iff referred type is resolved
+       Referred type's field might be not resolved */
+    bool isResolved() const { return mResolved != nullptr; }
+
+    T* operator->() { return get(); }
+    const T* operator->() const { return get(); }
+
+    /* Returns referenced object.
+       If a type is referenced, all typedefs are unwrapped. */
+    T* get() {
+        CHECK(mResolved != nullptr);
+        return mResolved->resolve();
+    }
+    const T* get() const {
+        CHECK(mResolved != nullptr);
+        return mResolved->resolve();
+    }
+
+    /* Returns exact referenced object.
+       If a type is referenced, typedefs are not unwraped. */
+    T* shallowGet() {
+        CHECK(mResolved != nullptr);
+        return mResolved;
+    }
+    const T* shallowGet() const {
+        CHECK(mResolved != nullptr);
+        return mResolved;
+    }
+
+    void set(T* resolved) {
+        CHECK(!isResolved());
+        CHECK(resolved != nullptr);
+        mResolved = resolved;
+    }
+
+    /* Returns true iff this is reference to null:
+       not resolved and has not name for lookup */
+    bool isEmptyReference() const { return !isResolved() && !hasLookupFqName(); }
+
+    const FQName& getLookupFqName() const {
+        CHECK(hasLookupFqName());
+        return mFqName;
+    }
+
+    bool hasLocation() const { return mLocation.isValid(); }
+
+    const Location& location() const {
+        CHECK(hasLocation());
+        return mLocation;
+    }
+
+   private:
+    /* Referred type */
+    T* mResolved = nullptr;
+    /* Reference name for lookup */
+    FQName mFqName;
+    /* Reference location is mainly used for printing errors
+       and handling forward reference restrictions */
+    Location mLocation;
+
+    bool hasLookupFqName() const {
+        // Valid only while not resolved to prevent confusion when
+        // ref.hasLookupFqName() is false while ref,get()->fqName is valid.
+        CHECK(!isResolved());
+        return mFqName.isValid();
+    }
+
+    template <class OtherT>
+    friend struct Reference;
+};
+
+template <class T>
+struct NamedReference : public Reference<T>, DocCommentable {
+    NamedReference(const std::string& name, const Reference<T>& reference, const Location& location)
+        : Reference<T>(reference, location), mName(name) {}
+
+    const std::string& name() const { return mName; }
+
+    // TODO(b/64715470) Legacy
+    const T& type() const { return *Reference<T>::get(); }
+
+   private:
+    const std::string mName;
+};
+
+}  // namespace android
+
+#endif  // REFERENCE_H_
diff --git a/ScalarType.cpp b/ScalarType.cpp
index fefd418..2e2e7b2 100644
--- a/ScalarType.cpp
+++ b/ScalarType.cpp
@@ -20,9 +20,7 @@
 
 namespace android {
 
-ScalarType::ScalarType(Kind kind)
-    : mKind(kind) {
-}
+ScalarType::ScalarType(Kind kind, Scope* parent) : Type(parent), mKind(kind) {}
 
 const ScalarType *ScalarType::resolveToScalarType() const {
     return this;
@@ -41,7 +39,7 @@
     return true;
 }
 
-bool ScalarType::canCheckEquality() const {
+bool ScalarType::deepCanCheckEquality(std::unordered_set<const Type*>* /* visited */) const {
     return true;
 }
 
@@ -279,10 +277,9 @@
         << ");\n";
 }
 
-status_t ScalarType::emitVtsTypeDeclarations(Formatter &out) const {
+void ScalarType::emitVtsTypeDeclarations(Formatter& out) const {
     out << "type: " << getVtsType() << "\n";
     out << "scalar_type: \"" << getVtsScalarType() << "\"\n";
-    return OK;
 }
 
 void ScalarType::getAlignmentAndSize(size_t *align, size_t *size) const {
diff --git a/ScalarType.h b/ScalarType.h
index 737da9a..86305b8 100644
--- a/ScalarType.h
+++ b/ScalarType.h
@@ -37,14 +37,14 @@
         KIND_DOUBLE,
     };
 
-    ScalarType(Kind kind);
+    ScalarType(Kind kind, Scope* parent);
 
     bool isScalar() const override;
 
     bool isElidableType() const override;
     const ScalarType *resolveToScalarType() const override;
 
-    bool canCheckEquality() const override;
+    bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
 
     std::string typeName() const override;
     bool isValidEnumStorageType() const;
@@ -96,7 +96,7 @@
             const std::string &offset,
             bool isReader) const override;
 
-    status_t emitVtsTypeDeclarations(Formatter &out) const override;
+    void emitVtsTypeDeclarations(Formatter& out) const override;
 
     void getAlignmentAndSize(size_t *align, size_t *size) const override;
 
diff --git a/Scope.cpp b/Scope.cpp
index 13a6f28..bff097f 100644
--- a/Scope.cpp
+++ b/Scope.cpp
@@ -16,42 +16,44 @@
 
 #include "Scope.h"
 
+#include "Annotation.h"
+#include "ConstantExpression.h"
 #include "Interface.h"
 
 #include <android-base/logging.h>
 #include <hidl-util/Formatter.h>
+#include <hidl-util/StringHelper.h>
+#include <algorithm>
+#include <iostream>
 #include <vector>
 
 namespace android {
 
-Scope::Scope(const char* localName, const Location& location, Scope* parent)
-    : NamedType(localName, location, parent) {}
+Scope::Scope(const char* localName, const FQName& fullName, const Location& location, Scope* parent)
+    : NamedType(localName, fullName, location, parent) {}
 Scope::~Scope(){}
 
-bool Scope::addType(NamedType *type, std::string *errorMsg) {
-    const std::string &localName = type->localName();
-
-    auto it = mTypeIndexByName.find(localName);
-
-    if (it != mTypeIndexByName.end()) {
-        *errorMsg = "A type named '";
-        (*errorMsg) += localName;
-        (*errorMsg) += "' is already declared in the  current scope.";
-
-        return false;
-    }
-
+void Scope::addType(NamedType* type) {
     size_t index = mTypes.size();
     mTypes.push_back(type);
-    mTypeIndexByName[localName] = index;
+    mTypeIndexByName[type->localName()] = index;
+}
 
-    return true;
+status_t Scope::validateUniqueNames() const {
+    for (const auto* type : mTypes) {
+        if (mTypes[mTypeIndexByName.at(type->localName())] != type) {
+            std::cerr << "ERROR: A type named '" << type->localName()
+                      << "' is already declared in the scope at " << type->location() << std::endl;
+            return UNKNOWN_ERROR;
+        }
+    }
+    return OK;
 }
 
 NamedType *Scope::lookupType(const FQName &fqName) const {
     CHECK(fqName.package().empty() && fqName.version().empty());
     if (!fqName.valueName().empty()) {
-        LOG(WARNING) << fqName.string() << " does not refer to a type.";
+        std::cerr << "ERROR: " << fqName.string() << " does not refer to a type." << std::endl;
         return nullptr;
     }
     std::vector<std::string> names = fqName.names();
@@ -73,7 +75,8 @@
     Scope *outerScope = static_cast<Scope *>(outerType);
     // *slowly* pop first element
     names.erase(names.begin());
-    FQName innerName(names);
+    FQName innerName;
+    CHECK(FQName::parse(StringHelper::JoinStrings(names, "."), &innerName));
     return outerScope->lookupType(innerName);
 }
 
@@ -103,96 +106,144 @@
     return false;
 }
 
-status_t Scope::forEachType(std::function<status_t(Type *)> func) const {
-    for (size_t i = 0; i < mTypes.size(); ++i) {
-        status_t err = func(mTypes[i]);
+const std::vector<Annotation*>& Scope::annotations() const {
+    return mAnnotations;
+}
 
-        if (err != OK) {
-            return err;
-        }
+void Scope::setAnnotations(std::vector<Annotation*>* annotations) {
+    CHECK(mAnnotations.empty());
+    CHECK(annotations != nullptr);
+    mAnnotations = *annotations;
+}
+
+std::vector<const Type*> Scope::getDefinedTypes() const {
+    std::vector<const Type*> ret;
+    ret.insert(ret.end(), mTypes.begin(), mTypes.end());
+    return ret;
+}
+
+std::vector<const ConstantExpression*> Scope::getConstantExpressions() const {
+    std::vector<const ConstantExpression*> ret;
+    for (const auto* annotation : mAnnotations) {
+        const auto& retAnnotation = annotation->getConstantExpressions();
+        ret.insert(ret.end(), retAnnotation.begin(), retAnnotation.end());
+    }
+    return ret;
+}
+
+void Scope::topologicalReorder(const std::unordered_map<const Type*, size_t>& reversedOrder) {
+    auto less = [&](const Type* lhs, const Type* rhs) {
+        return reversedOrder.at(lhs) < reversedOrder.at(rhs);
+    };
+
+    if (std::is_sorted(mTypes.begin(), mTypes.end(), less)) return;
+
+    mTypeOrderChanged = true;
+    std::sort(mTypes.begin(), mTypes.end(), less);
+
+    for (size_t i = 0; i != mTypes.size(); ++i) {
+        mTypeIndexByName.at(mTypes[i]->localName()) = i;
+    }
+}
+
+void Scope::emitTypeDeclarations(Formatter& out) const {
+    if (mTypes.empty()) return;
+
+    out << "// Forward declaration for forward reference support:\n";
+    for (const Type* type : mTypes) {
+        type->emitTypeForwardDeclaration(out);
+    }
+    out << "\n";
+
+    if (mTypeOrderChanged) {
+        out << "// Order of inner types was changed for forward reference support.\n\n";
     }
 
-    return OK;
+    for (const Type* type : mTypes) {
+        type->emitDocComment(out);
+        type->emitTypeDeclarations(out);
+    }
 }
 
-status_t Scope::emitTypeDeclarations(Formatter &out) const {
-    return forEachType([&](Type *type) {
-        return type->emitTypeDeclarations(out);
-    });
+void Scope::emitGlobalTypeDeclarations(Formatter& out) const {
+    for (const Type* type : mTypes) {
+        type->emitGlobalTypeDeclarations(out);
+    }
 }
 
-status_t Scope::emitGlobalTypeDeclarations(Formatter &out) const {
-    return forEachType([&](Type *type) {
-        return type->emitGlobalTypeDeclarations(out);
-    });
+void Scope::emitPackageTypeDeclarations(Formatter& out) const {
+    for (const Type* type : mTypes) {
+        type->emitPackageTypeDeclarations(out);
+    }
 }
 
-status_t Scope::emitGlobalHwDeclarations(Formatter &out) const {
-    return forEachType([&](Type *type) {
-        return type->emitGlobalHwDeclarations(out);
-    });
+void Scope::emitPackageHwDeclarations(Formatter& out) const {
+    for (const Type* type : mTypes) {
+        type->emitPackageHwDeclarations(out);
+    }
 }
 
-status_t Scope::emitJavaTypeDeclarations(
-        Formatter &out, bool atTopLevel) const {
-    return forEachType([&](Type *type) {
-        return type->emitJavaTypeDeclarations(out, atTopLevel);
-    });
+void Scope::emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const {
+    if (mTypeOrderChanged) {
+        out << "// Order of inner types was changed for forward reference support.\n\n";
+    }
+
+    for (const Type* type : mTypes) {
+        type->emitDocComment(out);
+        type->emitJavaTypeDeclarations(out, atTopLevel);
+    }
 }
 
-status_t Scope::emitTypeDefinitions(
-        Formatter &out, const std::string prefix) const {
-    return forEachType([&](Type *type) {
-        return type->emitTypeDefinitions(out, prefix);
-    });
+void Scope::emitTypeDefinitions(Formatter& out, const std::string& prefix) const {
+    for (const Type* type : mTypes) {
+        type->emitTypeDefinitions(out, prefix);
+    }
 }
 
 const std::vector<NamedType *> &Scope::getSubTypes() const {
     return mTypes;
 }
 
-status_t Scope::emitVtsTypeDeclarations(Formatter &out) const {
-    return forEachType([&](Type *type) {
-        return type->emitVtsTypeDeclarations(out);
-    });
+void Scope::emitVtsTypeDeclarations(Formatter& out) const {
+    for (const Type* type : mTypes) {
+        type->emitVtsTypeDeclarations(out);
+    }
 }
 
-bool Scope::isJavaCompatible() const {
-    for (const auto &type : mTypes) {
-        if (!type->isJavaCompatible()) {
+bool Scope::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
+    for (const Type* type : mTypes) {
+        if (!type->isJavaCompatible(visited)) {
             return false;
         }
     }
-
-    return true;
-}
-
-bool Scope::containsPointer() const {
-    for (const auto &type : mTypes) {
-        if (type->containsPointer()) {
-            return true;
-        }
-    }
-
-    return false;
+    return Type::deepIsJavaCompatible(visited);
 }
 
 void Scope::appendToExportedTypesVector(
         std::vector<const Type *> *exportedTypes) const {
-    forEachType([&](Type *type) {
+    for (const Type* type : mTypes) {
         type->appendToExportedTypesVector(exportedTypes);
-        return OK;
-    });
+    }
 }
 
-RootScope::RootScope(const char* localName, const Location& location, Scope* parent)
-    : Scope(localName, location, parent) {}
+////////////////////////////////////////
+
+RootScope::RootScope(const char* localName, const FQName& fullName, const Location& location,
+                     Scope* parent)
+    : Scope(localName, fullName, location, parent) {}
 RootScope::~RootScope() {}
 
 std::string RootScope::typeName() const {
     return "(root scope)";
 }
 
+status_t RootScope::validate() const {
+    CHECK(annotations().empty());
+    return Scope::validate();
+}
+
+////////////////////////////////////////
+
 LocalIdentifier::LocalIdentifier(){}
 LocalIdentifier::~LocalIdentifier(){}
 
@@ -200,5 +251,17 @@
     return false;
 }
 
+const LocalIdentifier* LocalIdentifier::resolve() const {
+    return this;
+}
+
+LocalIdentifier* LocalIdentifier::resolve() {
+    return this;
+}
+
+ConstantExpression* LocalIdentifier::constExpr() const {
+    return nullptr;
+}
+
 }  // namespace android
 
diff --git a/Scope.h b/Scope.h
index 599182f..e8c9431 100644
--- a/Scope.h
+++ b/Scope.h
@@ -21,19 +21,24 @@
 #include "NamedType.h"
 
 #include <map>
+#include <unordered_map>
 #include <vector>
 
 namespace android {
 
+struct Annotation;
+struct ConstantExpression;
 struct Formatter;
 struct Interface;
 struct LocalIdentifier;
 
 struct Scope : public NamedType {
-    Scope(const char* localName, const Location& location, Scope* parent);
+    Scope(const char* localName, const FQName& fullName, const Location& location, Scope* parent);
     virtual ~Scope();
 
-    bool addType(NamedType *type, std::string *errorMsg);
+    void addType(NamedType* type);
+
+    status_t validateUniqueNames() const;
 
     // lookup a type given an FQName.
     // Assume fqName.package(), fqName.version(), fqName.valueName() is empty.
@@ -48,39 +53,51 @@
 
     bool containsInterfaces() const;
 
-    status_t emitTypeDeclarations(Formatter &out) const override;
-    status_t emitGlobalTypeDeclarations(Formatter &out) const override;
-    status_t emitGlobalHwDeclarations(Formatter &out) const override;
+    const std::vector<Annotation*>& annotations() const;
 
-    status_t emitJavaTypeDeclarations(
-            Formatter &out, bool atTopLevel) const override;
+    void setAnnotations(std::vector<Annotation*>* annotations);
 
-    status_t emitTypeDefinitions(
-            Formatter &out, const std::string prefix) const override;
+    std::vector<const Type*> getDefinedTypes() const override;
+
+    std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+    void topologicalReorder(const std::unordered_map<const Type*, size_t>& reversedOrder);
+
+    void emitTypeDeclarations(Formatter& out) const override;
+    void emitGlobalTypeDeclarations(Formatter& out) const override;
+    void emitPackageTypeDeclarations(Formatter& out) const override;
+    void emitPackageHwDeclarations(Formatter& out) const override;
+
+    void emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const override;
+
+    void emitTypeDefinitions(Formatter& out, const std::string& prefix) const override;
 
     const std::vector<NamedType *> &getSubTypes() const;
 
-    status_t emitVtsTypeDeclarations(Formatter &out) const override;
+    void emitVtsTypeDeclarations(Formatter& out) const override;
 
-    bool isJavaCompatible() const override;
-    bool containsPointer() const override;
+    bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
 
     void appendToExportedTypesVector(
             std::vector<const Type *> *exportedTypes) const override;
 
-private:
+   private:
     std::vector<NamedType *> mTypes;
     std::map<std::string, size_t> mTypeIndexByName;
+    std::vector<Annotation*> mAnnotations;
 
-    status_t forEachType(std::function<status_t(Type *)> func) const;
+    bool mTypeOrderChanged = false;
 
     DISALLOW_COPY_AND_ASSIGN(Scope);
 };
 
 struct RootScope : public Scope {
-    RootScope(const char* localName, const Location& location, Scope* parent);
+    RootScope(const char* localName, const FQName& fullName, const Location& location,
+              Scope* parent);
     virtual ~RootScope();
 
+    virtual status_t validate() const override;
+
     std::string typeName() const override;
 };
 
@@ -88,6 +105,11 @@
     LocalIdentifier();
     virtual ~LocalIdentifier();
     virtual bool isEnumValue() const;
+
+    const LocalIdentifier* resolve() const;
+    LocalIdentifier* resolve();
+
+    virtual ConstantExpression* constExpr() const;
 };
 
 }  // namespace android
diff --git a/StringType.cpp b/StringType.cpp
index 7a60277..a07086f 100644
--- a/StringType.cpp
+++ b/StringType.cpp
@@ -22,13 +22,13 @@
 
 namespace android {
 
-StringType::StringType() {}
+StringType::StringType(Scope* parent) : Type(parent) {}
 
 bool StringType::isString() const {
     return true;
 }
 
-bool StringType::canCheckEquality() const {
+bool StringType::deepCanCheckEquality(std::unordered_set<const Type*>* /* visited */) const {
     return true;
 }
 
@@ -209,9 +209,8 @@
     return true;
 }
 
-status_t StringType::emitVtsTypeDeclarations(Formatter &out) const {
+void StringType::emitVtsTypeDeclarations(Formatter& out) const {
     out << "type: " << getVtsType() << "\n";
-    return OK;
 }
 
 static HidlTypeAssertion assertion("hidl_string", 16 /* size */);
diff --git a/StringType.h b/StringType.h
index 36299f7..a4e3691 100644
--- a/StringType.h
+++ b/StringType.h
@@ -23,11 +23,11 @@
 namespace android {
 
 struct StringType : public Type {
-    StringType();
+    StringType(Scope* parent);
 
     bool isString() const override;
 
-    bool canCheckEquality() const override;
+    bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
 
     std::string typeName() const override;
 
@@ -77,7 +77,7 @@
     bool needsEmbeddedReadWrite() const override;
     bool resultNeedsDeref() const override;
 
-    status_t emitVtsTypeDeclarations(Formatter &out) const override;
+    void emitVtsTypeDeclarations(Formatter& out) const override;
 
     void getAlignmentAndSize(size_t *align, size_t *size) const override;
 };
diff --git a/Type.cpp b/Type.cpp
index ea8df98..84be36f 100644
--- a/Type.cpp
+++ b/Type.cpp
@@ -16,28 +16,22 @@
 
 #include "Type.h"
 
-#include "Annotation.h"
+#include "ConstantExpression.h"
+#include "NamedType.h"
 #include "ScalarType.h"
+#include "Scope.h"
 
-#include <hidl-util/Formatter.h>
 #include <android-base/logging.h>
+#include <hidl-util/Formatter.h>
+#include <algorithm>
+#include <iostream>
 
 namespace android {
 
-Type::Type()
-    : mAnnotations(nullptr) {
-}
+Type::Type(Scope* parent) : mParent(parent) {}
 
 Type::~Type() {}
 
-void Type::setAnnotations(std::vector<Annotation *> *annotations) {
-    mAnnotations = annotations;
-}
-
-const std::vector<Annotation *> &Type::annotations() const {
-    return *mAnnotations;
-}
-
 bool Type::isScope() const {
     return false;
 }
@@ -102,6 +96,228 @@
     return false;
 }
 
+Type* Type::resolve() {
+    return const_cast<Type*>(static_cast<const Type*>(this)->resolve());
+}
+
+const Type* Type::resolve() const {
+    return this;
+}
+
+std::vector<Type*> Type::getDefinedTypes() {
+    const auto& constRet = static_cast<const Type*>(this)->getDefinedTypes();
+    std::vector<Type*> ret(constRet.size());
+    std::transform(constRet.begin(), constRet.end(), ret.begin(),
+                   [](const auto* type) { return const_cast<Type*>(type); });
+    return ret;
+}
+
+std::vector<const Type*> Type::getDefinedTypes() const {
+    return {};
+}
+
+std::vector<Reference<Type>*> Type::getReferences() {
+    const auto& constRet = static_cast<const Type*>(this)->getReferences();
+    std::vector<Reference<Type>*> ret(constRet.size());
+    std::transform(constRet.begin(), constRet.end(), ret.begin(),
+                   [](const auto* ref) { return const_cast<Reference<Type>*>(ref); });
+    return ret;
+}
+
+std::vector<const Reference<Type>*> Type::getReferences() const {
+    return {};
+}
+
+std::vector<ConstantExpression*> Type::getConstantExpressions() {
+    const auto& constRet = static_cast<const Type*>(this)->getConstantExpressions();
+    std::vector<ConstantExpression*> ret(constRet.size());
+    std::transform(constRet.begin(), constRet.end(), ret.begin(),
+                   [](const auto* ce) { return const_cast<ConstantExpression*>(ce); });
+    return ret;
+}
+
+std::vector<const ConstantExpression*> Type::getConstantExpressions() const {
+    return {};
+}
+
+std::vector<Reference<Type>*> Type::getStrongReferences() {
+    const auto& constRet = static_cast<const Type*>(this)->getStrongReferences();
+    std::vector<Reference<Type>*> ret(constRet.size());
+    std::transform(constRet.begin(), constRet.end(), ret.begin(),
+                   [](const auto* ref) { return const_cast<Reference<Type>*>(ref); });
+    return ret;
+}
+
+std::vector<const Reference<Type>*> Type::getStrongReferences() const {
+    std::vector<const Reference<Type>*> ret;
+    for (const auto* ref : getReferences()) {
+        if (!ref->shallowGet()->isNeverStrongReference()) {
+            ret.push_back(ref);
+        }
+    }
+    return ret;
+}
+
+status_t Type::recursivePass(const std::function<status_t(Type*)>& func,
+                             std::unordered_set<const Type*>* visited) {
+    if (mIsPostParseCompleted) return OK;
+
+    if (visited->find(this) != visited->end()) return OK;
+    visited->insert(this);
+
+    status_t err = func(this);
+    if (err != OK) return err;
+
+    for (auto* nextType : getDefinedTypes()) {
+        err = nextType->recursivePass(func, visited);
+        if (err != OK) return err;
+    }
+
+    for (auto* nextRef : getReferences()) {
+        err = nextRef->shallowGet()->recursivePass(func, visited);
+        if (err != OK) return err;
+    }
+
+    return OK;
+}
+
+status_t Type::recursivePass(const std::function<status_t(const Type*)>& func,
+                             std::unordered_set<const Type*>* visited) const {
+    if (mIsPostParseCompleted) return OK;
+
+    if (visited->find(this) != visited->end()) return OK;
+    visited->insert(this);
+
+    status_t err = func(this);
+    if (err != OK) return err;
+
+    for (const auto* nextType : getDefinedTypes()) {
+        err = nextType->recursivePass(func, visited);
+        if (err != OK) return err;
+    }
+
+    for (const auto* nextRef : getReferences()) {
+        err = nextRef->shallowGet()->recursivePass(func, visited);
+        if (err != OK) return err;
+    }
+
+    return OK;
+}
+
+status_t Type::resolveInheritance() {
+    return OK;
+}
+
+status_t Type::validate() const {
+    return OK;
+}
+
+Type::CheckAcyclicStatus::CheckAcyclicStatus(status_t status, const Type* cycleEnd)
+    : status(status), cycleEnd(cycleEnd) {
+    CHECK(cycleEnd == nullptr || status != OK);
+}
+
+Type::CheckAcyclicStatus Type::topologicalOrder(
+    std::unordered_map<const Type*, size_t>* reversedOrder,
+    std::unordered_set<const Type*>* stack) const {
+    if (stack->find(this) != stack->end()) {
+        std::cerr << "ERROR: Cyclic declaration:\n";
+        return CheckAcyclicStatus(UNKNOWN_ERROR, this);
+    }
+
+    if (reversedOrder->find(this) != reversedOrder->end()) return CheckAcyclicStatus(OK);
+    stack->insert(this);
+
+    for (const auto* nextType : getDefinedTypes()) {
+        auto err = nextType->topologicalOrder(reversedOrder, stack);
+
+        if (err.status != OK) {
+            if (err.cycleEnd == nullptr) return err;
+
+            std::cerr << "  '" << nextType->typeName() << "' in '" << typeName() << "'";
+            if (nextType->isNamedType()) {
+                std::cerr << " at " << static_cast<const NamedType*>(nextType)->location();
+            }
+            std::cerr << "\n";
+
+            if (err.cycleEnd == this) {
+                return CheckAcyclicStatus(err.status);
+            }
+            return err;
+        }
+    }
+
+    for (const auto* nextRef : getStrongReferences()) {
+        const auto* nextType = nextRef->shallowGet();
+        auto err = nextType->topologicalOrder(reversedOrder, stack);
+
+        if (err.status != OK) {
+            if (err.cycleEnd == nullptr) return err;
+
+            std::cerr << "  '" << nextType->typeName() << "' in '" << typeName() << "' at "
+                      << nextRef->location() << "\n";
+
+            if (err.cycleEnd == this) {
+                return CheckAcyclicStatus(err.status);
+            }
+            return err;
+        }
+    }
+
+    CHECK(stack->find(this) != stack->end());
+    stack->erase(this);
+
+    CHECK(reversedOrder->find(this) == reversedOrder->end());
+    // Do not call insert and size in one statement to not rely on
+    // evaluation order.
+    size_t index = reversedOrder->size();
+    reversedOrder->insert({this, index});
+
+    return CheckAcyclicStatus(OK);
+}
+
+status_t Type::checkForwardReferenceRestrictions(const Reference<Type>& ref) const {
+    const Location& refLoc = ref.location();
+    const Type* refType = ref.shallowGet();
+
+    // Not NamedTypes are avaiable everywhere.
+    // Only ArrayType and TemplatedType contain additional types in
+    // their reference (which is actually a part of type definition),
+    // so they are proceeded in this case.
+    //
+    // If we support named templated types one day, we will need to change
+    // this logic.
+    if (!refType->isNamedType()) {
+        for (const Reference<Type>* innerRef : refType->getReferences()) {
+            status_t err = checkForwardReferenceRestrictions(*innerRef);
+            if (err != OK) return err;
+        }
+        return OK;
+    }
+
+    const Location& typeLoc = static_cast<const NamedType*>(refType)->location();
+
+    // If referenced type is declared in another file or before reference,
+    // there is no forward reference here.
+    if (!Location::inSameFile(refLoc, typeLoc) ||
+        (!Location::intersect(refLoc, typeLoc) && typeLoc < refLoc)) {
+        return OK;
+    }
+
+    // Type must be declared somewhere in the current stack to make it
+    // available for forward referencing.
+    const Type* refTypeParent = refType->parent();
+    for (const Type* ancestor = this; ancestor != nullptr; ancestor = ancestor->parent()) {
+        if (ancestor == refTypeParent) return OK;
+    }
+
+    std::cerr << "ERROR: Forward reference of '" << refType->typeName() << "' at " << ref.location()
+              << " is not supported.\n"
+              << "C++ forward declaration doesn't support inner types.\n";
+
+    return UNKNOWN_ERROR;
+}
+
 const ScalarType *Type::resolveToScalarType() const {
     return NULL;
 }
@@ -121,9 +337,36 @@
 }
 
 bool Type::canCheckEquality() const {
+    std::unordered_set<const Type*> visited;
+    return canCheckEquality(&visited);
+}
+
+bool Type::canCheckEquality(std::unordered_set<const Type*>* visited) const {
+    // See isJavaCompatible for similar structure.
+    if (visited->find(this) != visited->end()) {
+        return true;
+    }
+    visited->insert(this);
+    return deepCanCheckEquality(visited);
+}
+
+bool Type::deepCanCheckEquality(std::unordered_set<const Type*>* /* visited */) const {
     return false;
 }
 
+void Type::setPostParseCompleted() {
+    CHECK(!mIsPostParseCompleted);
+    mIsPostParseCompleted = true;
+}
+
+Scope* Type::parent() {
+    return mParent;
+}
+
+const Scope* Type::parent() const {
+    return mParent;
+}
+
 std::string Type::getCppType(StorageMode, bool) const {
     CHECK(!"Should not be here");
     return std::string();
@@ -370,36 +613,43 @@
     handleError(out, mode);
 }
 
-status_t Type::emitTypeDeclarations(Formatter &) const {
-    return OK;
-}
+void Type::emitTypeDeclarations(Formatter&) const {}
 
-status_t Type::emitGlobalTypeDeclarations(Formatter &) const {
-    return OK;
-}
+void Type::emitTypeForwardDeclaration(Formatter&) const {}
 
-status_t Type::emitGlobalHwDeclarations(Formatter &) const {
-    return OK;
-}
+void Type::emitGlobalTypeDeclarations(Formatter&) const {}
 
-status_t Type::emitTypeDefinitions(
-        Formatter &, const std::string) const {
-    return OK;
-}
+void Type::emitPackageTypeDeclarations(Formatter&) const {}
 
-status_t Type::emitJavaTypeDeclarations(Formatter &, bool) const {
-    return OK;
-}
+void Type::emitPackageHwDeclarations(Formatter&) const {}
+
+void Type::emitTypeDefinitions(Formatter&, const std::string&) const {}
+
+void Type::emitJavaTypeDeclarations(Formatter&, bool) const {}
 
 bool Type::needsEmbeddedReadWrite() const {
     return false;
 }
 
-bool Type::needsResolveReferences() const {
+bool Type::resultNeedsDeref() const {
     return false;
 }
 
-bool Type::resultNeedsDeref() const {
+bool Type::needsResolveReferences() const {
+    std::unordered_set<const Type*> visited;
+    return needsResolveReferences(&visited);
+}
+
+bool Type::needsResolveReferences(std::unordered_set<const Type*>* visited) const {
+    // See isJavaCompatible for similar structure.
+    if (visited->find(this) != visited->end()) {
+        return false;
+    }
+    visited->insert(this);
+    return deepNeedsResolveReferences(visited);
+}
+
+bool Type::deepNeedsResolveReferences(std::unordered_set<const Type*>* /* visited */) const {
     return false;
 }
 
@@ -438,79 +688,124 @@
     out << ");\n";
 }
 
-status_t Type::emitVtsTypeDeclarations(Formatter &) const {
-    return OK;
-}
+void Type::emitVtsTypeDeclarations(Formatter&) const {}
 
-status_t Type::emitVtsAttributeType(Formatter &out) const {
-    return emitVtsTypeDeclarations(out);
+void Type::emitVtsAttributeType(Formatter& out) const {
+    emitVtsTypeDeclarations(out);
 }
 
 bool Type::isJavaCompatible() const {
+    std::unordered_set<const Type*> visited;
+    return isJavaCompatible(&visited);
+}
+
+bool Type::containsPointer() const {
+    std::unordered_set<const Type*> visited;
+    return containsPointer(&visited);
+}
+
+bool Type::isJavaCompatible(std::unordered_set<const Type*>* visited) const {
+    // We need to find al least one path from requested vertex
+    // to not java compatible.
+    // That means that if we have already visited some vertex,
+    // there is no need to determine whether it is java compatible
+    // (and we can assume that it is java compatible),
+    // as if not, the information about that would appear in the
+    // requested vertex through another path.
+    if (visited->find(this) != visited->end()) {
+        return true;
+    }
+    visited->insert(this);
+    return deepIsJavaCompatible(visited);
+}
+
+bool Type::containsPointer(std::unordered_set<const Type*>* visited) const {
+    // See isJavaCompatible for similar structure.
+    if (visited->find(this) != visited->end()) {
+        return false;
+    }
+    visited->insert(this);
+    return deepContainsPointer(visited);
+}
+
+bool Type::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
     return true;
 }
 
+bool Type::deepContainsPointer(std::unordered_set<const Type*>* /* visited */) const {
+    return false;
+}
+
 void Type::getAlignmentAndSize(
         size_t * /* align */, size_t * /* size */) const {
     CHECK(!"Should not be here.");
 }
 
-bool Type::containsPointer() const {
-    return false;
-}
-
 void Type::appendToExportedTypesVector(
         std::vector<const Type *> * /* exportedTypes */) const {
 }
 
-status_t Type::emitExportedHeader(
-        Formatter & /* out */, bool /* forJava */) const {
-    return OK;
+void Type::emitExportedHeader(Formatter& /* out */, bool /* forJava */) const {}
+
+bool Type::isNeverStrongReference() const {
+    return false;
 }
 
 ////////////////////////////////////////
 
-TemplatedType::TemplatedType() : mElementType(nullptr) {
+TemplatedType::TemplatedType(Scope* parent) : Type(parent) {}
+
+std::string TemplatedType::typeName() const {
+    return templatedTypeName() + " of " + mElementType->typeName();
 }
 
-void TemplatedType::setElementType(Type *elementType) {
-    CHECK(mElementType == nullptr); // can only be set once.
-    CHECK(isCompatibleElementType(elementType));
+void TemplatedType::setElementType(const Reference<Type>& elementType) {
+    // can only be set once.
+    CHECK(mElementType.isEmptyReference());
+    CHECK(!elementType.isEmptyReference());
+
     mElementType = elementType;
 }
 
-Type *TemplatedType::getElementType() const {
-    return mElementType;
+const Type* TemplatedType::getElementType() const {
+    return mElementType.get();
 }
 
 bool TemplatedType::isTemplatedType() const {
     return true;
 }
 
-status_t TemplatedType::emitVtsTypeDeclarations(Formatter &out) const {
-    out << "type: " << getVtsType() << "\n";
-    out << getVtsValueName() << ": {\n";
-    out.indent();
-    status_t err = mElementType->emitVtsTypeDeclarations(out);
-    if (err != OK) {
-        return err;
-    }
-    out.unindent();
-    out << "}\n";
-    return OK;
+std::vector<const Reference<Type>*> TemplatedType::getReferences() const {
+    return {&mElementType};
 }
 
-status_t TemplatedType::emitVtsAttributeType(Formatter &out) const {
+status_t TemplatedType::validate() const {
+    if (!isCompatibleElementType(mElementType.get())) {
+        std::cerr << "ERROR: " << typeName() /* contains element type */
+                  << " is not supported at " << mElementType.location() << "\n";
+        return UNKNOWN_ERROR;
+    }
+
+    return Type::validate();
+}
+
+void TemplatedType::emitVtsTypeDeclarations(Formatter& out) const {
     out << "type: " << getVtsType() << "\n";
     out << getVtsValueName() << ": {\n";
     out.indent();
-    status_t status = mElementType->emitVtsAttributeType(out);
-    if (status != OK) {
-        return status;
-    }
+    mElementType->emitVtsTypeDeclarations(out);
     out.unindent();
     out << "}\n";
-    return OK;
 }
+
+void TemplatedType::emitVtsAttributeType(Formatter& out) const {
+    out << "type: " << getVtsType() << "\n";
+    out << getVtsValueName() << ": {\n";
+    out.indent();
+    mElementType->emitVtsAttributeType(out);
+    out.unindent();
+    out << "}\n";
+}
+
 }  // namespace android
 
diff --git a/Type.h b/Type.h
index 8e139d4..a0ed5bc 100644
--- a/Type.h
+++ b/Type.h
@@ -19,20 +19,26 @@
 #define TYPE_H_
 
 #include <android-base/macros.h>
-#include <string>
 #include <utils/Errors.h>
-#include <vector>
 #include <set>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "DocComment.h"
+#include "Reference.h"
 
 namespace android {
 
-struct Annotation;
+struct ConstantExpression;
 struct Formatter;
-struct ScalarType;
 struct FQName;
+struct ScalarType;
+struct Scope;
 
-struct Type {
-    Type();
+struct Type : DocCommentable {
+    Type(Scope* parent);
     virtual ~Type();
 
     virtual bool isArray() const;
@@ -52,13 +58,82 @@
     virtual bool isTypeDef() const;
     virtual bool isVector() const;
 
+    // Resolves the type by unwrapping typedefs
+    Type* resolve();
+    virtual const Type* resolve() const;
+
+    // All types defined in this type.
+    std::vector<Type*> getDefinedTypes();
+    virtual std::vector<const Type*> getDefinedTypes() const;
+
+    // All types referenced in this type.
+    std::vector<Reference<Type>*> getReferences();
+    virtual std::vector<const Reference<Type>*> getReferences() const;
+
+    // All constant expressions referenced in this type.
+    std::vector<ConstantExpression*> getConstantExpressions();
+    virtual std::vector<const ConstantExpression*> getConstantExpressions() const;
+
+    // All types referenced in this type that must have completed
+    // definiton before being referenced.
+    std::vector<Reference<Type>*> getStrongReferences();
+    virtual std::vector<const Reference<Type>*> getStrongReferences() const;
+
+    // Proceeds recursive pass
+    // Makes sure to visit each node only once.
+    status_t recursivePass(const std::function<status_t(Type*)>& func,
+                           std::unordered_set<const Type*>* visited);
+    status_t recursivePass(const std::function<status_t(const Type*)>& func,
+                           std::unordered_set<const Type*>* visited) const;
+
+    // Recursive tree pass that completes type declarations
+    // that depend on super types
+    virtual status_t resolveInheritance();
+
+    // Recursive tree pass that validates all type-related
+    // syntax restrictions
+    virtual status_t validate() const;
+
+    // Recursive tree pass checkAcyclic return type.
+    // Stores cycle end for nice error messages.
+    struct CheckAcyclicStatus {
+        CheckAcyclicStatus(status_t status, const Type* cycleEnd = nullptr);
+
+        status_t status;
+
+        // If a cycle is found, stores the end of cycle.
+        // While going back in recursion, this is used to stop printing the cycle.
+        const Type* cycleEnd;
+    };
+
+    // Recursive tree pass that ensures that type definitions and references
+    // are acyclic and builds reversed topological order of the types.
+    // If some cases allow using of incomplete types, these cases are to be
+    // declared in Type::getStrongReferences.
+    CheckAcyclicStatus topologicalOrder(std::unordered_map<const Type*, size_t>* reversedOrder,
+                                        std::unordered_set<const Type*>* stack) const;
+
+    // Checks following C++ restriction on forward declaration:
+    // inner struct could be forward declared only inside its parent.
+    status_t checkForwardReferenceRestrictions(const Reference<Type>& ref) const;
+
     virtual const ScalarType *resolveToScalarType() const;
 
     virtual std::string typeName() const = 0;
 
     bool isValidEnumStorageType() const;
     virtual bool isElidableType() const;
+
     virtual bool canCheckEquality() const;
+    bool canCheckEquality(std::unordered_set<const Type*>* visited) const;
+    virtual bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const;
+
+    // Marks that package proceeding is completed
+    // Post parse passes must be proceeded during owner package parsing
+    void setPostParseCompleted();
+
+    Scope* parent();
+    const Scope* parent() const;
 
     enum StorageMode {
         StorageMode_Stack,
@@ -176,47 +251,65 @@
             const std::string &offset,
             bool isReader) const;
 
-    virtual status_t emitTypeDeclarations(Formatter &out) const;
+    virtual void emitTypeDeclarations(Formatter& out) const;
+
+    virtual void emitGlobalTypeDeclarations(Formatter& out) const;
+
+    // Emit scope C++ forward declaration.
+    // There is no need to forward declare interfaces, as
+    // they are always declared in global scope in dedicated file.
+    virtual void emitTypeForwardDeclaration(Formatter& out) const;
 
     // Emit any declarations pertaining to this type that have to be
     // at global scope, i.e. enum class operators.
-    virtual status_t emitGlobalTypeDeclarations(Formatter &out) const;
+    // For android.hardware.foo@1.0::*, this will be in namespace
+    // android::hardware::foo::V1_0
+    virtual void emitPackageTypeDeclarations(Formatter& out) const;
 
     // Emit any declarations pertaining to this type that have to be
     // at global scope for transport, e.g. read/writeEmbeddedTo/FromParcel
-    virtual status_t emitGlobalHwDeclarations(Formatter &out) const;
+    // For android.hardware.foo@1.0::*, this will be in namespace
+    // android::hardware::foo::V1_0
+    virtual void emitPackageHwDeclarations(Formatter& out) const;
 
-    virtual status_t emitTypeDefinitions(
-            Formatter &out, const std::string prefix) const;
+    virtual void emitTypeDefinitions(Formatter& out, const std::string& prefix) const;
 
-    virtual status_t emitJavaTypeDeclarations(
-            Formatter &out, bool atTopLevel) const;
+    virtual void emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const;
 
     virtual bool needsEmbeddedReadWrite() const;
-    virtual bool needsResolveReferences() const;
     virtual bool resultNeedsDeref() const;
 
+    bool needsResolveReferences() const;
+    bool needsResolveReferences(std::unordered_set<const Type*>* visited) const;
+    virtual bool deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const;
+
     // Generates type declaration for vts proto file.
     // TODO (b/30844146): make it a pure virtual method.
-    virtual status_t emitVtsTypeDeclarations(Formatter &out) const;
+    virtual void emitVtsTypeDeclarations(Formatter& out) const;
     // Generates type declaration as attribute of method (return value or method
     // argument) or attribute of compound type for vts proto file.
-    virtual status_t emitVtsAttributeType(Formatter &out) const;
+    virtual void emitVtsAttributeType(Formatter& out) const;
 
     // Returns true iff this type is supported through the Java backend.
-    virtual bool isJavaCompatible() const;
-    virtual bool containsPointer() const;
-    virtual void getAlignmentAndSize(size_t *align, size_t *size) const;
+    bool isJavaCompatible() const;
+    bool isJavaCompatible(std::unordered_set<const Type*>* visited) const;
+    virtual bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const;
+    // Returns true iff type contains pointer
+    // (excluding methods and inner types).
+    bool containsPointer() const;
+    bool containsPointer(std::unordered_set<const Type*>* visited) const;
+    virtual bool deepContainsPointer(std::unordered_set<const Type*>* visited) const;
 
-    void setAnnotations(std::vector<Annotation *> *annotations);
-    const std::vector<Annotation *> &annotations() const;
+    virtual void getAlignmentAndSize(size_t *align, size_t *size) const;
 
     virtual void appendToExportedTypesVector(
             std::vector<const Type *> *exportedTypes) const;
 
-    virtual status_t emitExportedHeader(Formatter &out, bool forJava) const;
+    virtual void emitExportedHeader(Formatter& out, bool forJava) const;
 
-protected:
+    virtual bool isNeverStrongReference() const;
+
+   protected:
     void handleError(Formatter &out, ErrorMode mode) const;
 
     void emitReaderWriterEmbeddedForTypeName(
@@ -247,24 +340,37 @@
             const std::string &methodName,
             const std::string &name) const;
 
-private:
-    std::vector<Annotation *> *mAnnotations;
+   private:
+    bool mIsPostParseCompleted = false;
+    Scope* const mParent;
 
     DISALLOW_COPY_AND_ASSIGN(Type);
 };
 
 /* Base type for VectorType and RefType. */
 struct TemplatedType : public Type {
-    void setElementType(Type *elementType);
-    Type *getElementType() const;
+    void setElementType(const Reference<Type>& elementType);
+    const Type* getElementType() const;
+
+    virtual std::string templatedTypeName() const = 0;
+    std::string typeName() const override;
+
     bool isTemplatedType() const override;
-    virtual bool isCompatibleElementType(Type *elementType) const = 0;
-    status_t emitVtsTypeDeclarations(Formatter &out) const override;
-    status_t emitVtsAttributeType(Formatter &out) const override;
-protected:
-    TemplatedType();
-    Type *mElementType;
-private:
+
+    virtual bool isCompatibleElementType(const Type* elementType) const = 0;
+
+    std::vector<const Reference<Type>*> getReferences() const override;
+
+    virtual status_t validate() const override;
+
+    void emitVtsTypeDeclarations(Formatter& out) const override;
+    void emitVtsAttributeType(Formatter& out) const override;
+
+   protected:
+    TemplatedType(Scope* parent);
+    Reference<Type> mElementType;
+
+   private:
     DISALLOW_COPY_AND_ASSIGN(TemplatedType);
 };
 
diff --git a/TypeDef.cpp b/TypeDef.cpp
index f419efe..dcefc57 100644
--- a/TypeDef.cpp
+++ b/TypeDef.cpp
@@ -21,16 +21,21 @@
 
 namespace android {
 
-TypeDef::TypeDef(const char* localName, const Location& location, Scope* parent, Type* type)
-    : NamedType(localName, location, parent), mReferencedType(type) {}
+TypeDef::TypeDef(const char* localName, const FQName& fullName, const Location& location,
+                 Scope* parent, const Reference<Type>& type)
+    : NamedType(localName, fullName, location, parent), mReferencedType(type) {}
 
 const ScalarType *TypeDef::resolveToScalarType() const {
     CHECK(!"Should not be here");
     return NULL;
 }
 
-Type *TypeDef::referencedType() const {
-    return mReferencedType;
+Type* TypeDef::referencedType() {
+    return mReferencedType.get();
+}
+
+const Type* TypeDef::referencedType() const {
+    return mReferencedType.get();
 }
 
 bool TypeDef::isInterface() const {
@@ -50,6 +55,14 @@
     return true;
 }
 
+const Type* TypeDef::resolve() const {
+    return mReferencedType.get();
+}
+
+std::vector<const Reference<Type>*> TypeDef::getReferences() const {
+    return {&mReferencedType};
+}
+
 bool TypeDef::needsEmbeddedReadWrite() const {
     CHECK(!"Should not be here");
     return false;
@@ -60,14 +73,12 @@
     return false;
 }
 
-status_t TypeDef::emitTypeDeclarations(Formatter &out) const {
+void TypeDef::emitTypeDeclarations(Formatter& out) const {
     out << "typedef "
         << mReferencedType->getCppStackType()
         << " "
         << localName()
         << ";\n\n";
-
-    return OK;
 }
 
 }  // namespace android
diff --git a/TypeDef.h b/TypeDef.h
index 7914363..5ebba3e 100644
--- a/TypeDef.h
+++ b/TypeDef.h
@@ -23,13 +23,15 @@
 namespace android {
 
 struct TypeDef : public NamedType {
-    TypeDef(const char* localName, const Location& location, Scope* parent, Type* type);
+    TypeDef(const char* localName, const FQName& fullName, const Location& location, Scope* parent,
+            const Reference<Type>& type);
 
     const ScalarType *resolveToScalarType() const override;
 
     std::string typeName() const override;
 
-    Type *referencedType() const;
+    Type* referencedType();
+    const Type* referencedType() const;
 
     bool isInterface() const override;
     bool isEnum() const override;
@@ -37,10 +39,14 @@
     bool needsEmbeddedReadWrite() const override;
     bool resultNeedsDeref() const override;
 
-    status_t emitTypeDeclarations(Formatter &out) const override;
+    const Type* resolve() const override;
 
-private:
-    Type *mReferencedType;
+    std::vector<const Reference<Type>*> getReferences() const override;
+
+    void emitTypeDeclarations(Formatter& out) const override;
+
+   private:
+    Reference<Type> mReferencedType;
 
     DISALLOW_COPY_AND_ASSIGN(TypeDef);
 };
diff --git a/VectorType.cpp b/VectorType.cpp
index 4d1dae8..03604dc 100644
--- a/VectorType.cpp
+++ b/VectorType.cpp
@@ -25,14 +25,13 @@
 
 namespace android {
 
-VectorType::VectorType() {
+VectorType::VectorType(Scope* parent) : TemplatedType(parent) {}
+
+std::string VectorType::templatedTypeName() const {
+    return "vector";
 }
 
-std::string VectorType::typeName() const {
-    return "vector" + (mElementType == nullptr ? "" : (" of " + mElementType->typeName()));
-}
-
-bool VectorType::isCompatibleElementType(Type *elementType) const {
+bool VectorType::isCompatibleElementType(const Type* elementType) const {
     if (elementType->isScalar()) {
         return true;
     }
@@ -45,8 +44,7 @@
     if (elementType->isBitField()) {
         return true;
     }
-    if (elementType->isCompoundType()
-            && static_cast<CompoundType *>(elementType)->style() == CompoundType::STYLE_STRUCT) {
+    if (elementType->isCompoundType()) {
         return true;
     }
     if (elementType->isInterface()) {
@@ -59,11 +57,11 @@
         return true;
     }
     if (elementType->isTemplatedType()) {
-        Type *inner = static_cast<TemplatedType *>(elementType)->getElementType();
+        const Type* inner = static_cast<const TemplatedType*>(elementType)->getElementType();
         return this->isCompatibleElementType(inner) && !inner->isInterface();
     }
     if (elementType->isArray()) {
-        Type *inner = static_cast<ArrayType *>(elementType)->getElementType();
+        const Type* inner = static_cast<const ArrayType*>(elementType)->getElementType();
         return this->isCompatibleElementType(inner) && !inner->isInterface();
     }
     return false;
@@ -77,8 +75,12 @@
     return mElementType->isBinder();
 }
 
-bool VectorType::canCheckEquality() const {
-    return mElementType->canCheckEquality();
+bool VectorType::deepCanCheckEquality(std::unordered_set<const Type*>* visited) const {
+    return mElementType->canCheckEquality(visited);
+}
+
+std::vector<const Reference<Type>*> VectorType::getStrongReferences() const {
+    return {};
 }
 
 std::string VectorType::getCppType(StorageMode mode,
@@ -564,7 +566,7 @@
     VectorType::EmitJavaFieldReaderWriterForElementType(
             out,
             depth,
-            mElementType,
+            mElementType.get(),
             parcelName,
             blobName,
             fieldName,
@@ -711,21 +713,24 @@
     return true;
 }
 
-bool VectorType::needsResolveReferences() const {
-    return mElementType->needsResolveReferences();
+bool VectorType::deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const {
+    if (mElementType->needsResolveReferences(visited)) {
+        return true;
+    }
+    return TemplatedType::deepNeedsResolveReferences(visited);
 }
 
 bool VectorType::resultNeedsDeref() const {
     return !isVectorOfBinders();
 }
 
-bool VectorType::isJavaCompatible() const {
-    if (!mElementType->isJavaCompatible()) {
+bool VectorType::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
+    if (!mElementType->isJavaCompatible(visited)) {
         return false;
     }
 
     if (mElementType->isArray()) {
-        return static_cast<ArrayType *>(mElementType)->countDimensions() == 1;
+        return static_cast<const ArrayType*>(mElementType.get())->countDimensions() == 1;
     }
 
     if (mElementType->isVector()) {
@@ -736,11 +741,14 @@
         return false;
     }
 
-    return true;
+    return TemplatedType::deepIsJavaCompatible(visited);
 }
 
-bool VectorType::containsPointer() const {
-    return mElementType->containsPointer();
+bool VectorType::deepContainsPointer(std::unordered_set<const Type*>* visited) const {
+    if (mElementType->containsPointer(visited)) {
+        return true;
+    }
+    return TemplatedType::deepContainsPointer(visited);
 }
 
 // All hidl_vec<T> have the same size.
diff --git a/VectorType.h b/VectorType.h
index e868355..6f6ede7 100644
--- a/VectorType.h
+++ b/VectorType.h
@@ -18,19 +18,25 @@
 
 #define VECTOR_TYPE_H_
 
+#include <vector>
+
+#include "Reference.h"
 #include "Type.h"
 
 namespace android {
 
 struct VectorType : public TemplatedType {
-    VectorType();
+    VectorType(Scope* parent);
 
     bool isVector() const override;
     bool isVectorOfBinders() const;
-    std::string typeName() const override;
-    bool isCompatibleElementType(Type *elementType) const override;
 
-    bool canCheckEquality() const override;
+    std::string templatedTypeName() const override;
+    bool isCompatibleElementType(const Type* elementType) const override;
+
+    std::vector<const Reference<Type>*> getStrongReferences() const override;
+
+    bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
 
     std::string getCppType(
             StorageMode mode,
@@ -115,11 +121,11 @@
             bool isReader);
 
     bool needsEmbeddedReadWrite() const override;
-    bool needsResolveReferences() const override;
+    bool deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const override;
     bool resultNeedsDeref() const override;
 
-    bool isJavaCompatible() const override;
-    bool containsPointer() const override;
+    bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
+    bool deepContainsPointer(std::unordered_set<const Type*>* visited) const override;
 
     void getAlignmentAndSize(size_t *align, size_t *size) const override;
     static void getAlignmentAndSizeStatic(size_t *align, size_t *size);
diff --git a/build/Android.bp b/build/Android.bp
new file mode 100644
index 0000000..9b290a3
--- /dev/null
+++ b/build/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+bootstrap_go_package {
+    name: "hidl-soong-rules",
+    pkgPath: "android/soong/hidl",
+    deps: [
+        "blueprint",
+        "soong",
+        "soong-android",
+        "soong-cc",
+        "soong-genrule",
+        "soong-java",
+    ],
+    srcs: [
+        "fqName.go",
+        "hidl_interface.go",
+        "hidl_package_root.go",
+        "properties.go",
+        "utils.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/build/fqName.go b/build/fqName.go
new file mode 100644
index 0000000..88f7dc0
--- /dev/null
+++ b/build/fqName.go
@@ -0,0 +1,126 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hidl
+
+import (
+	"errors"
+	"path/filepath"
+	"regexp"
+	"strings"
+)
+
+type fqName struct {
+	packageComponents []string
+	minor             string
+	major             string
+}
+
+// See system/tools/hidl/FQName.h
+var component = "[a-zA-Z_][a-zA-Z_0-9]*"
+var pkg = "(" + component + "(?:[.]" + component + ")*)"
+var digits = "([0-9]+)"
+var re_package = regexp.MustCompile("^" + pkg + "@" + digits + "." + digits + "$")
+
+func parseFqName(f string) (*fqName, error) {
+	matches := re_package.FindAllStringSubmatch(f, 3)
+
+	if matches == nil {
+		return nil, errors.New("Poorly formed hal interface name: '" + f + "' must match '" + re_package.String() + "'")
+	}
+
+	ret := fqName{
+		packageComponents: strings.Split(matches[0][1], "."),
+		major:             matches[0][2],
+		minor:             matches[0][3],
+	}
+
+	return &ret, nil
+}
+
+func (f *fqName) inPackage(pkg string) bool {
+	components := strings.Split(pkg, ".")
+
+	if len(components) > len(f.packageComponents) {
+		return false
+	}
+
+	for i, v := range components {
+		if f.packageComponents[i] != v {
+			return false
+		}
+	}
+
+	return true
+}
+
+func (f *fqName) pkg() string {
+	return strings.Join(f.packageComponents, ".")
+}
+func (f *fqName) version() string {
+	return f.major + "." + f.minor
+}
+func (f *fqName) sanitizedVersion() string {
+	return "V" + f.major + "_" + f.minor
+}
+func (f *fqName) string() string {
+	return f.pkg() + "@" + f.version()
+}
+func (f *fqName) sanitizedString() string {
+	return f.pkg() + "-V" + f.version() // NOT sanitizedVersion
+}
+
+func (f *fqName) dir() string {
+	return filepath.Join(filepath.Join(f.packageComponents...), f.version()) + "/"
+}
+func (f *fqName) sanitizedDir() string {
+	return filepath.Join(filepath.Join(f.packageComponents...), f.sanitizedVersion()) + "/"
+}
+
+func (f *fqName) fileGroupName() string {
+	return f.string() + "_hal"
+}
+func (f *fqName) sourcesName() string {
+	return f.string() + "_genc++"
+}
+func (f *fqName) headersName() string {
+	return f.string() + "_genc++_headers"
+}
+func (f *fqName) javaName() string {
+	return f.sanitizedString() + "-java"
+}
+func (f *fqName) javaSourcesName() string {
+	return f.sanitizedString() + "-java_gen_java"
+}
+func (f *fqName) javaConstantsName() string {
+	return f.sanitizedString() + "-java-constants"
+}
+func (f *fqName) javaConstantsSourcesName() string {
+	return f.sanitizedString() + "-java-constants_gen_java"
+}
+func (f *fqName) adapterName() string {
+	return f.string() + "-adapter"
+}
+func (f *fqName) adapterSourcesName() string {
+	return f.string() + "-adapter_genc++"
+}
+func (f *fqName) adapterHelperName() string {
+	return f.string() + "-adapter-helper"
+}
+func (f *fqName) adapterHelperSourcesName() string {
+	return f.string() + "-adapter-helper_genc++"
+}
+func (f *fqName) adapterHelperHeadersName() string {
+	return f.string() + "-adapter-helper_genc++_headers"
+}
diff --git a/build/hidl_interface.go b/build/hidl_interface.go
new file mode 100644
index 0000000..55cba94
--- /dev/null
+++ b/build/hidl_interface.go
@@ -0,0 +1,428 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hidl
+
+import (
+	"strings"
+	"sync"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	"android/soong/cc"
+	"android/soong/genrule"
+	"android/soong/java"
+)
+
+var (
+	hidlInterfaceSuffix = "_interface"
+)
+
+func init() {
+	android.RegisterModuleType("hidl_interface", hidlInterfaceFactory)
+}
+
+type hidlInterfaceProperties struct {
+	// Vndk properties for interface library only.
+	cc.VndkProperties
+
+	// The owner of the module
+	Owner *string
+
+	// List of .hal files which compose this interface.
+	Srcs []string
+
+	// List of hal interface packages that this library depends on.
+	Interfaces []string
+
+	// Package root for this package, must be a prefix of name
+	Root string
+
+	// List of non-TypeDef types declared in types.hal.
+	Types []string
+
+	// Whether to generate the Java library stubs.
+	// Default: true
+	Gen_java *bool
+
+	// Whether to generate a Java library containing constants
+	// expressed by @export annotations in the hal files.
+	Gen_java_constants bool
+
+	// Don't generate "android.hidl.foo@1.0" C library. Instead
+	// only generate the genrules so that this package can be
+	// included in libhidltransport.
+	Core_interface bool
+}
+
+type hidlInterface struct {
+	android.ModuleBase
+
+	properties hidlInterfaceProperties
+}
+
+func processSources(mctx android.LoadHookContext, srcs []string) ([]string, []string, bool) {
+	var interfaces []string
+	var types []string // hidl-gen only supports types.hal, but don't assume that here
+
+	hasError := false
+
+	for _, v := range srcs {
+		if !strings.HasSuffix(v, ".hal") {
+			mctx.PropertyErrorf("srcs", "Source must be a .hal file: "+v)
+			hasError = true
+			continue
+		}
+
+		name := strings.TrimSuffix(v, ".hal")
+
+		if strings.HasPrefix(name, "I") {
+			baseName := strings.TrimPrefix(name, "I")
+			interfaces = append(interfaces, baseName)
+		} else {
+			types = append(types, name)
+		}
+	}
+
+	return interfaces, types, !hasError
+}
+
+func processDependencies(mctx android.LoadHookContext, interfaces []string) ([]string, []string, bool) {
+	var dependencies []string
+	var javaDependencies []string
+
+	hasError := false
+
+	for _, v := range interfaces {
+		name, err := parseFqName(v)
+		if err != nil {
+			mctx.PropertyErrorf("interfaces", err.Error())
+			hasError = true
+			continue
+		}
+		dependencies = append(dependencies, name.string())
+		javaDependencies = append(javaDependencies, name.javaName())
+	}
+
+	return dependencies, javaDependencies, !hasError
+}
+
+func getRootList(mctx android.LoadHookContext, interfaces []string) ([]string, bool) {
+	var roots []string
+	hasError := false
+
+	for _, i := range interfaces {
+		interfaceObject := lookupInterface(i)
+		if interfaceObject == nil {
+			mctx.PropertyErrorf("interfaces", "Cannot find interface "+i)
+			hasError = true
+			continue
+		}
+		root := interfaceObject.properties.Root
+		rootObject := lookupPackageRoot(root)
+		if rootObject == nil {
+			mctx.PropertyErrorf("interfaces", `Cannot find package root specification for package `+
+				`root '%s' needed for module '%s'. Either this is a mispelling of the package `+
+				`root, or a new hidl_package_root module needs to be added. For example, you can `+
+				`fix this error by adding the following to <some path>/Android.bp:
+
+hidl_package_root {
+    name: "%s",
+    path: "<some path>",
+}
+
+This corresponds to the "-r%s:<some path>" option that would be passed into hidl-gen.`, root, i, root, root)
+			hasError = true
+			continue
+		}
+
+		roots = append(roots, root+":"+rootObject.properties.Path)
+	}
+
+	return android.FirstUniqueStrings(roots), !hasError
+}
+
+func removeCoreDependencies(mctx android.LoadHookContext, dependencies []string) ([]string, bool) {
+	var ret []string
+	hasError := false
+
+	for _, i := range dependencies {
+		interfaceObject := lookupInterface(i)
+		if interfaceObject == nil {
+			mctx.PropertyErrorf("interfaces", "Cannot find interface "+i)
+			hasError = true
+			continue
+		}
+
+		if !interfaceObject.properties.Core_interface {
+			ret = append(ret, i)
+		}
+	}
+
+	return ret, !hasError
+}
+
+func hidlGenCommand(lang string, roots []string, name *fqName) *string {
+	cmd := "$(location hidl-gen) -d $(depfile) -o $(genDir)"
+	cmd += " -L" + lang
+	cmd += " " + strings.Join(wrap("-r", roots, ""), " ")
+	cmd += " " + name.string()
+	return &cmd
+}
+
+func hidlInterfaceMutator(mctx android.LoadHookContext, i *hidlInterface) {
+	name, err := parseFqName(i.ModuleBase.Name())
+	if err != nil {
+		mctx.PropertyErrorf("name", err.Error())
+	}
+
+	if !name.inPackage(i.properties.Root) {
+		mctx.PropertyErrorf("root", "Root, "+i.properties.Root+", for "+name.string()+" must be a prefix.")
+	}
+
+	interfaces, types, _ := processSources(mctx, i.properties.Srcs)
+
+	if len(interfaces) == 0 && len(types) == 0 {
+		mctx.PropertyErrorf("srcs", "No sources provided.")
+	}
+
+	dependencies, javaDependencies, _ := processDependencies(mctx, i.properties.Interfaces)
+	roots, _ := getRootList(mctx, append(dependencies, name.string()))
+	cppDependencies, _ := removeCoreDependencies(mctx, dependencies)
+
+	if mctx.Failed() {
+		return
+	}
+
+	shouldGenerateLibrary := !i.properties.Core_interface
+	// explicitly true if not specified to give early warning to devs
+	shouldGenerateJava := i.properties.Gen_java == nil || *i.properties.Gen_java
+	shouldGenerateJavaConstants := i.properties.Gen_java_constants
+
+	var libraryIfExists []string
+	if shouldGenerateLibrary {
+		libraryIfExists = []string{name.string()}
+	}
+
+	// TODO(b/69002743): remove filegroups
+	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.FileGroupFactory), &fileGroupProperties{
+		Name:  proptools.StringPtr(name.fileGroupName()),
+		Owner: i.properties.Owner,
+		Srcs:  i.properties.Srcs,
+	})
+
+	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+		Name:    proptools.StringPtr(name.sourcesName()),
+		Depfile: proptools.BoolPtr(true),
+		Owner:   i.properties.Owner,
+		Tools:   []string{"hidl-gen"},
+		Cmd:     hidlGenCommand("c++-sources", roots, name),
+		Srcs:    i.properties.Srcs,
+		Out: concat(wrap(name.dir(), interfaces, "All.cpp"),
+			wrap(name.dir(), types, ".cpp")),
+	})
+	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+		Name:    proptools.StringPtr(name.headersName()),
+		Depfile: proptools.BoolPtr(true),
+		Owner:   i.properties.Owner,
+		Tools:   []string{"hidl-gen"},
+		Cmd:     hidlGenCommand("c++-headers", roots, name),
+		Srcs:    i.properties.Srcs,
+		Out: concat(wrap(name.dir()+"I", interfaces, ".h"),
+			wrap(name.dir()+"Bs", interfaces, ".h"),
+			wrap(name.dir()+"BnHw", interfaces, ".h"),
+			wrap(name.dir()+"BpHw", interfaces, ".h"),
+			wrap(name.dir()+"IHw", interfaces, ".h"),
+			wrap(name.dir(), types, ".h"),
+			wrap(name.dir()+"hw", types, ".h")),
+	})
+
+	if shouldGenerateLibrary {
+		mctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &ccProperties{
+			Name:              proptools.StringPtr(name.string()),
+			Owner:             i.properties.Owner,
+			Vendor_available:  proptools.BoolPtr(true),
+			Defaults:          []string{"hidl-module-defaults"},
+			Generated_sources: []string{name.sourcesName()},
+			Generated_headers: []string{name.headersName()},
+			Shared_libs: concat(cppDependencies, []string{
+				"libhidlbase",
+				"libhidltransport",
+				"libhwbinder",
+				"liblog",
+				"libutils",
+				"libcutils",
+			}),
+			Export_shared_lib_headers: concat(cppDependencies, []string{
+				"libhidlbase",
+				"libhidltransport",
+				"libhwbinder",
+				"libutils",
+			}),
+			Export_generated_headers: []string{name.headersName()},
+		}, &i.properties.VndkProperties)
+	}
+
+	if shouldGenerateJava {
+		mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+			Name:    proptools.StringPtr(name.javaSourcesName()),
+			Depfile: proptools.BoolPtr(true),
+			Owner:   i.properties.Owner,
+			Tools:   []string{"hidl-gen"},
+			Cmd:     hidlGenCommand("java", roots, name),
+			Srcs:    i.properties.Srcs,
+			Out: concat(wrap(name.sanitizedDir()+"I", interfaces, ".java"),
+				wrap(name.sanitizedDir(), i.properties.Types, ".java")),
+		})
+		mctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory(true)), &javaProperties{
+			Name:              proptools.StringPtr(name.javaName()),
+			Owner:             i.properties.Owner,
+			Sdk_version:       proptools.StringPtr("system_current"),
+			Defaults:          []string{"hidl-java-module-defaults"},
+			No_framework_libs: proptools.BoolPtr(true),
+			Srcs:              []string{":" + name.javaSourcesName()},
+			Static_libs:       append(javaDependencies, "hwbinder"),
+		})
+	}
+
+	if shouldGenerateJavaConstants {
+		mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+			Name:    proptools.StringPtr(name.javaConstantsSourcesName()),
+			Depfile: proptools.BoolPtr(true),
+			Owner:   i.properties.Owner,
+			Tools:   []string{"hidl-gen"},
+			Cmd:     hidlGenCommand("java-constants", roots, name),
+			Srcs:    i.properties.Srcs,
+			Out:     []string{name.sanitizedDir() + "Constants.java"},
+		})
+		mctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory(true)), &javaProperties{
+			Name:              proptools.StringPtr(name.javaConstantsName()),
+			Owner:             i.properties.Owner,
+			Defaults:          []string{"hidl-java-module-defaults"},
+			No_framework_libs: proptools.BoolPtr(true),
+			Srcs:              []string{":" + name.javaConstantsSourcesName()},
+		})
+	}
+
+	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+		Name:    proptools.StringPtr(name.adapterHelperSourcesName()),
+		Depfile: proptools.BoolPtr(true),
+		Owner:   i.properties.Owner,
+		Tools:   []string{"hidl-gen"},
+		Cmd:     hidlGenCommand("c++-adapter-sources", roots, name),
+		Srcs:    i.properties.Srcs,
+		Out:     wrap(name.dir()+"A", concat(interfaces, types), ".cpp"),
+	})
+	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+		Name:    proptools.StringPtr(name.adapterHelperHeadersName()),
+		Depfile: proptools.BoolPtr(true),
+		Owner:   i.properties.Owner,
+		Tools:   []string{"hidl-gen"},
+		Cmd:     hidlGenCommand("c++-adapter-headers", roots, name),
+		Srcs:    i.properties.Srcs,
+		Out:     wrap(name.dir()+"A", concat(interfaces, types), ".h"),
+	})
+
+	mctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &ccProperties{
+		Name:              proptools.StringPtr(name.adapterHelperName()),
+		Owner:             i.properties.Owner,
+		Vendor_available:  proptools.BoolPtr(true),
+		Defaults:          []string{"hidl-module-defaults"},
+		Generated_sources: []string{name.adapterHelperSourcesName()},
+		Generated_headers: []string{name.adapterHelperHeadersName()},
+		Shared_libs: []string{
+			"libbase",
+			"libcutils",
+			"libhidlbase",
+			"libhidltransport",
+			"libhwbinder",
+			"liblog",
+			"libutils",
+		},
+		Static_libs: concat([]string{
+			"libhidladapter",
+		}, wrap("", dependencies, "-adapter-helper"), cppDependencies, libraryIfExists),
+		Export_shared_lib_headers: []string{
+			"libhidlbase",
+			"libhidltransport",
+		},
+		Export_static_lib_headers: concat([]string{
+			"libhidladapter",
+		}, wrap("", dependencies, "-adapter-helper"), cppDependencies, libraryIfExists),
+		Export_generated_headers: []string{name.adapterHelperHeadersName()},
+		Group_static_libs:        proptools.BoolPtr(true),
+	})
+	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+		Name:    proptools.StringPtr(name.adapterSourcesName()),
+		Depfile: proptools.BoolPtr(true),
+		Owner:   i.properties.Owner,
+		Tools:   []string{"hidl-gen"},
+		Cmd:     hidlGenCommand("c++-adapter-main", roots, name),
+		Srcs:    i.properties.Srcs,
+		Out:     []string{"main.cpp"},
+	})
+	mctx.CreateModule(android.ModuleFactoryAdaptor(cc.TestFactory), &ccProperties{
+		Name:              proptools.StringPtr(name.adapterName()),
+		Owner:             i.properties.Owner,
+		Generated_sources: []string{name.adapterSourcesName()},
+		Shared_libs: []string{
+			"libbase",
+			"libcutils",
+			"libhidlbase",
+			"libhidltransport",
+			"libhwbinder",
+			"liblog",
+			"libutils",
+		},
+		Static_libs: concat([]string{
+			"libhidladapter",
+			name.adapterHelperName(),
+		}, wrap("", dependencies, "-adapter-helper"), cppDependencies, libraryIfExists),
+		Group_static_libs: proptools.BoolPtr(true),
+	})
+}
+
+func (h *hidlInterface) Name() string {
+	return h.ModuleBase.Name() + hidlInterfaceSuffix
+}
+func (h *hidlInterface) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+func (h *hidlInterface) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
+var hidlInterfaceMutex sync.Mutex
+var hidlInterfaces []*hidlInterface
+
+func hidlInterfaceFactory() android.Module {
+	i := &hidlInterface{}
+	i.AddProperties(&i.properties)
+	android.InitAndroidModule(i)
+	android.AddLoadHook(i, func(ctx android.LoadHookContext) { hidlInterfaceMutator(ctx, i) })
+
+	hidlInterfaceMutex.Lock()
+	hidlInterfaces = append(hidlInterfaces, i)
+	hidlInterfaceMutex.Unlock()
+
+	return i
+}
+
+func lookupInterface(name string) *hidlInterface {
+	for _, i := range hidlInterfaces {
+		if i.ModuleBase.Name() == name {
+			return i
+		}
+	}
+	return nil
+}
diff --git a/build/hidl_package_root.go b/build/hidl_package_root.go
new file mode 100644
index 0000000..1f68af4
--- /dev/null
+++ b/build/hidl_package_root.go
@@ -0,0 +1,63 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hidl
+
+import (
+	"sync"
+
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterModuleType("hidl_package_root", hidlPackageRootFactory)
+}
+
+type hidlPackageRoot struct {
+	android.ModuleBase
+
+	properties struct {
+		// path to this module from root
+		Path string
+	}
+}
+
+func (r *hidlPackageRoot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+func (r *hidlPackageRoot) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
+var packageRootsMutex sync.Mutex
+var packageRoots []*hidlPackageRoot
+
+func hidlPackageRootFactory() android.Module {
+	r := &hidlPackageRoot{}
+	r.AddProperties(&r.properties)
+	android.InitAndroidModule(r)
+
+	packageRootsMutex.Lock()
+	packageRoots = append(packageRoots, r)
+	packageRootsMutex.Unlock()
+
+	return r
+}
+
+func lookupPackageRoot(name string) *hidlPackageRoot {
+	for _, i := range packageRoots {
+		if i.ModuleBase.Name() == name {
+			return i
+		}
+	}
+	return nil
+}
diff --git a/build/properties.go b/build/properties.go
new file mode 100644
index 0000000..d2f8a35
--- /dev/null
+++ b/build/properties.go
@@ -0,0 +1,61 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hidl
+
+type nameProperties struct {
+	Name *string
+}
+
+type fileGroupProperties struct {
+	Name  *string
+	Owner *string
+	Srcs  []string
+}
+
+type genruleProperties struct {
+	Name    *string
+	Owner   *string
+	Tools   []string
+	Cmd     *string
+	Srcs    []string
+	Out     []string
+	Depfile *bool
+}
+
+type ccProperties struct {
+	Name                      *string
+	Owner                     *string
+	Defaults                  []string
+	Vendor_available          *bool
+	Generated_sources         []string
+	Generated_headers         []string
+	Group_static_libs         *bool
+	Shared_libs               []string
+	Static_libs               []string
+	Export_shared_lib_headers []string
+	Export_static_lib_headers []string
+	Export_generated_headers  []string
+}
+
+type javaProperties struct {
+	Name              *string
+	Owner             *string
+	Defaults          []string
+	No_framework_libs *bool
+	Sdk_version       *string
+	Srcs              []string
+	Libs              []string
+	Static_libs       []string
+}
diff --git a/build/utils.go b/build/utils.go
new file mode 100644
index 0000000..ba3ac8e
--- /dev/null
+++ b/build/utils.go
@@ -0,0 +1,43 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hidl
+
+// wrap(p, a, s) = [p + v + s for v in a]
+func wrap(prefix string, strs []string, suffix string) []string {
+	ret := make([]string, len(strs))
+	for i, v := range strs {
+		ret[i] = prefix + v + suffix
+	}
+	return ret
+}
+
+// concat(a...) = sum((i for i in a), [])
+func concat(sstrs ...[]string) []string {
+	var ret []string
+	for _, v := range sstrs {
+		ret = append(ret, v...)
+	}
+	return ret
+}
+
+func remove(str string, strs []string) []string {
+	var ret []string
+	for _, v := range strs {
+		if v != str {
+			ret = append(ret, v)
+		}
+	}
+	return ret
+}
diff --git a/c2hal/AST.cpp b/c2hal/AST.cpp
index e2ae13c..783a04e 100644
--- a/c2hal/AST.cpp
+++ b/c2hal/AST.cpp
@@ -29,7 +29,6 @@
 #include <string>
 #include <algorithm>
 #include <stdlib.h>
-#include <sys/dir.h>
 #include <sys/stat.h>
 
 namespace android {
@@ -317,7 +316,7 @@
 
     size_t start = 1;  // Ignore leading '/'
     size_t slashPos;
-    while ((slashPos = path.find("/", start)) != std::string::npos) {
+    while ((slashPos = path.find('/', start)) != std::string::npos) {
         std::string partial = path.substr(0, slashPos);
 
         struct stat st;
diff --git a/c2hal/Android.bp b/c2hal/Android.bp
index 03987f5..3dd9c6a 100644
--- a/c2hal/Android.bp
+++ b/c2hal/Android.bp
@@ -12,13 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-subdirs = [
-    "test",
-]
-
 cc_binary_host {
     name: "c2hal",
-    cflags: hidl_flags,
+    defaults: ["hidl-gen-defaults"],
     srcs: [
         "AST.cpp",
         "c2hal_l.ll",
diff --git a/c2hal/Expression.cpp b/c2hal/Expression.cpp
index 7863b15..0ab864f 100644
--- a/c2hal/Expression.cpp
+++ b/c2hal/Expression.cpp
@@ -29,7 +29,7 @@
 static const std::regex RE_S64("[^ul](l|ll)$");
 static const std::regex RE_U64("[^ul](ul|ull)$");
 
-Expression::Type Expression::integralType(std::string integer) {
+Expression::Type Expression::integralType(const std::string& integer) {
     if (std::regex_search(integer, RE_S32)) {
         return Type::S32;
     }
@@ -55,8 +55,8 @@
     // because we are reducing everything to two ranks, we can heavily simplify
     // conversion rules
 
-#define SIGNED(i) (i & 2) // i & 0b10
-#define MAX_RANK(i) (i | 1) // i | 0b01
+#define SIGNED(i) ((i) & 2) // i & 0b10
+#define MAX_RANK(i) ((i) | 1) // i | 0b01
 
     if (lhs == rhs) {
         return lhs;
diff --git a/c2hal/Expression.h b/c2hal/Expression.h
index 9f04837..5449e55 100644
--- a/c2hal/Expression.h
+++ b/c2hal/Expression.h
@@ -63,7 +63,7 @@
         }
     }
 
-    static Type integralType(std::string integer);
+    static Type integralType(const std::string& integer);
     static Type coalesceTypes(Type lhs, Type rhs);
 
     static Expression *parenthesize(Expression *inner);
diff --git a/c2hal/c2hal_l.ll b/c2hal/c2hal_l.ll
index d5e4511..f613adc 100644
--- a/c2hal/c2hal_l.ll
+++ b/c2hal/c2hal_l.ll
@@ -50,8 +50,6 @@
 
 int check_type(yyscan_t yyscanner, struct yyguts_t *yyg);
 
-// TODO convert to parse params/lex params
-
 extern int start_token;
 
 extern std::string last_comment;
@@ -80,6 +78,7 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wunused-parameter"
 #pragma clang diagnostic ignored "-Wdeprecated-register"
+#pragma clang diagnostic ignored "-Wregister"
 
 %}
 
diff --git a/c2hal/test/simple.h b/c2hal/test/simple.h
index e3d7f3e..701ebe4 100644
--- a/c2hal/test/simple.h
+++ b/c2hal/test/simple.h
@@ -32,7 +32,7 @@
 
 /* Simple example */
 typedef struct simple_t {
-    /**
+    /*
      * Common methods of the simple device.
      */
     struct hw_device_t common;
@@ -67,7 +67,7 @@
 
 } simple_location_t;
 
-/** convenience API for coloring */
+/* convenience API for coloring */
 
 static inline int showColor(const struct hw_module_t* module,
         struct simple_t** device) {
diff --git a/c2hal/test/test.h b/c2hal/test/test.h
index b1a0058..3a83b0c 100644
--- a/c2hal/test/test.h
+++ b/c2hal/test/test.h
@@ -34,7 +34,6 @@
                  get rid of magic numbers */
 
 /* test */
-/** test */
 /* test **/
 /* test / ** ** / test */
 /* test //// ***** test /****/
@@ -91,7 +90,6 @@
 
 #undef ONE
 
-/*****************************************************************************/
 typedef void (*no_arg_fun)(void);
 
 typedef int (*other_fun)(int j);
@@ -110,7 +108,7 @@
 int (*global_fun_2)(struct framebuffer_device_t* dev, int enable);
 
 typedef struct framebuffer_device_t {
-    /**
+    /*
      * Common methods of the framebuffer device.
      */
     struct hw_device_t common;
@@ -324,7 +322,7 @@
     float c;
 } that_t;
 
-/**
+/*
  * return the frame size (number of bytes per sample) of an output stream.
  */
 static inline size_t audio_stream_out_frame_size(const struct audio_stream_out *s)
diff --git a/docs/Android.bp b/docs/Android.bp
new file mode 100644
index 0000000..2d7d78b
--- /dev/null
+++ b/docs/Android.bp
@@ -0,0 +1,64 @@
+// Copyright (C) 2007 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//
+// hidl-doc
+//
+java_binary_host {
+    name: "hidl-doc",
+    manifest: "etc/manifest.txt",
+    srcs: [
+        "src/main.kt",
+        "src/writer/formatutils.kt",
+        "src/writer/elements/TypedefElement.kt",
+        "src/writer/elements/EnumElement.kt",
+        "src/writer/elements/AbstractElement.kt",
+        "src/writer/elements/CompoundElement.kt",
+        "src/writer/elements/MethodElement.kt",
+        "src/writer/files/AbstractFileWriter.kt",
+        "src/writer/files/InterfaceFileWriter.kt",
+        "src/writer/files/IndexFileWriter.kt",
+        "src/writer/files/TypesFileWriter.kt",
+        "src/writer/files/AbstractParserFileWriter.kt",
+        "src/writer/files/resources.kt",
+        "src/lexer/ILexer.kt",
+        "src/lexer/Token.kt",
+        "src/lexer/DocLexer.kt",
+        "src/lexer/HidlLexer.kt",
+        "src/parser/elements/EntryParser.kt",
+        "src/parser/elements/DocParser.kt",
+        "src/parser/elements/EntryCollectionParser.kt",
+        "src/parser/elements/AbstractParser.kt",
+        "src/parser/elements/DocAnnotationParser.kt",
+        "src/parser/elements/declarations/EnumDeclarationParser.kt",
+        "src/parser/elements/declarations/InterfaceDeclarationParser.kt",
+        "src/parser/elements/declarations/TypedefDeclarationParser.kt",
+        "src/parser/elements/declarations/MethodDeclarationParser.kt",
+        "src/parser/elements/declarations/AbstractDeclarationParser.kt",
+        "src/parser/elements/declarations/CompoundDeclarationParser.kt",
+        "src/parser/elements/AnnotationParser.kt",
+        "src/parser/utils.kt",
+        "src/parser/config.kt",
+        "src/parser/files/InterfaceFileParser.kt",
+        "src/parser/files/TypesFileParser.kt",
+        "src/parser/files/AbstractFileParser.kt",
+        "src/parser/files/package.kt",
+    ],
+    java_resources: [
+        "resources/template/index.html",
+        "resources/template/types.html",
+        "resources/template/interface.html",
+        "resources/assets/style.css",
+    ],
+}
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..9624451
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,35 @@
+# Install
+
+~~~
+$ m -j hidl-doc
+~~~
+
+# Usage
+
+View usage info:
+
+~~~
+$ ./bin/hidl-doc -h
+~~~
+
+Parse the audio `types.hal` file in the Android repo and output
+generated HTML reference to the reference directory. Enable verbose mode:
+
+~~~
+$ ./bin/hidl-doc -v -i /path/to/android/hardware/interfaces/audio/2.0/types.hal \
+  -o /path/to/output/en/reference/hidl/
+~~~
+
+Parse all HAL files in the Android `/hardware/interfaces/` directory
+and output generated HTML reference docs to reference directory. Skip files
+that encounter doc parse errors:
+
+~~~
+$ ./bin/hidl-doc -v -s -i /path/to/android/hardware/interfaces/ \
+  -o /path/to/output/en/reference/hidl/
+~~~
+# Templates
+
+HTML templates are used to generate the output docs and are in the
+`resources/template/` directory. Since these files are bundled up in the fat jar
+file, if you make any changes to the templates, `hidl-doc.jar` must be rebuilt.
diff --git a/docs/etc/manifest.txt b/docs/etc/manifest.txt
new file mode 100644
index 0000000..82888cf
--- /dev/null
+++ b/docs/etc/manifest.txt
@@ -0,0 +1 @@
+Main-Class: MainKt
\ No newline at end of file
diff --git a/docs/resources/assets/style.css b/docs/resources/assets/style.css
new file mode 100644
index 0000000..a9b8b16
--- /dev/null
+++ b/docs/resources/assets/style.css
@@ -0,0 +1,15 @@
+header #api-info-block {
+  float: right;
+  font-size: 0.9em;
+  color: #999;
+}
+
+header #api-info-block code {
+  font-size: 0.8em;
+  color: #37474f;
+}
+
+header code {
+  padding: 0;
+  background-color: transparent;
+}
\ No newline at end of file
diff --git a/docs/resources/template/index.html b/docs/resources/template/index.html
new file mode 100644
index 0000000..d4faee5
--- /dev/null
+++ b/docs/resources/template/index.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html devsite>
+<!--################    THIS FILE IS GENERATED     ################-->
+<!--################          DO NOT EDIT          ################-->
+<!--################  CHANGE THE HAL DOC COMMENTS  ################-->
+<head>
+  <title>$title | HIDL reference</title>
+  <meta name="project_path" value="/_project.yaml">
+  <meta name="book_path" value="/reference/hidl/_book.yaml">
+  <meta name="hide_page_heading" value="true">
+  <link href="/reference/hidl/assets/style.css" rel="stylesheet">
+</head>
+<body>
+  <header>
+    <h1 class="hide-from-toc">HIDL Reference</h1>
+    <p>
+      The HAL Interface Description Language (HIDL) specifies the interface
+      between a HAL and its users. It defines types and method calls, collected
+      into interfaces and packages. HIDL is a system for communicating between
+      codebases that may be compiled independently and is intended for
+      inter-process communication. See the
+      <a href="/devices/architecture/hidl">HIDL guides</a>.
+    </p>
+  </header>
+  <section>
+    <h2 class="hide-from-toc">Interfaces and types</h2>
+$entries
+  </section>
+</body>
+</html>
diff --git a/docs/resources/template/interface.html b/docs/resources/template/interface.html
new file mode 100644
index 0000000..feed37b
--- /dev/null
+++ b/docs/resources/template/interface.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html devsite>
+<!--################    THIS FILE IS GENERATED     ################-->
+<!--################          DO NOT EDIT          ################-->
+<!--################  CHANGE THE HAL DOC COMMENTS  ################-->
+<head>
+  <title>$name | HIDL reference</title>
+  <meta name="project_path" value="/_project.yaml">
+  <meta name="book_path" value="/reference/hidl/_book.yaml">
+  <meta name="hide_page_heading" value="true">
+  <link href="/reference/hidl/assets/style.css" rel="stylesheet">
+</head>
+<body>
+$header
+$propertyDefs
+$methodDefs
+</body>
+</html>
\ No newline at end of file
diff --git a/docs/resources/template/types.html b/docs/resources/template/types.html
new file mode 100644
index 0000000..d7d25a2
--- /dev/null
+++ b/docs/resources/template/types.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html devsite>
+<!--################    THIS FILE IS GENERATED     ################-->
+<!--################          DO NOT EDIT          ################-->
+<!--################  CHANGE THE HAL DOC COMMENTS  ################-->
+<head>
+  <title>$name | HIDL reference</title>
+  <meta name="project_path" value="/_project.yaml">
+  <meta name="book_path" value="/reference/hidl/_book.yaml">
+  <meta name="hide_page_heading" value="true">
+  <link href="/reference/hidl/assets/style.css" rel="stylesheet">
+</head>
+<body>
+$header
+$propertyDefs
+</body>
+</html>
\ No newline at end of file
diff --git a/docs/src/lexer/DocLexer.kt b/docs/src/lexer/DocLexer.kt
new file mode 100644
index 0000000..ec1a745
--- /dev/null
+++ b/docs/src/lexer/DocLexer.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package lexer
+
+import java.util.*
+
+object DocLexer : ILexer {
+
+    /**
+     * @param str The string should already be padded from the file lexer.
+     */
+    override fun tokenize(str: String): List<Token> {
+        val tokens = mutableListOf<Token>()
+        var token: String
+
+        //remove docblock comment indention - must go before others
+        val formattedStr = str.lines()
+                //remove docblock comment indent prefix (must go before others) ...
+                .map { it.replace(Regex("^\\s*\\*[^/]"), "") } //indented prefix except doc_end
+                .map { it.replace(Regex("^\\s*\\*$"), "") }    //prefix empty lines remain
+                //replace empty lines with something the scanner can pick out ...
+                .map { it.replace(Regex("$\\s*^"), TokenGrammar.EMPTY_LINE.value) }
+                .joinToString("\n")
+
+        Scanner(formattedStr).use { scanner ->
+            while (scanner.hasNext()) {
+                token = scanner.next()
+
+                when (token) {
+                    TokenGrammar.EMPTY_LINE.value -> tokens.add(TokenGrammar.newToken("", TokenGrammar.EMPTY_LINE))
+
+                    //if part of annotation, add following tag as well
+                    TokenGrammar.AT.value -> {
+                        tokens.add(TokenGrammar.newToken(token)) //'@'
+                        //determine if part of annotation
+                        for (annotation in TokenGrammar.docAnnotations()) {
+                            if (scanner.hasNext(annotation.value)) {
+                                tokens.add(TokenGrammar.newToken(scanner.next()))
+                            }
+                        }
+                    }
+
+                    //default to DocWord
+                    else -> {
+                        val id = TokenGrammar.getFromValueOrDefault(token)
+                        val category = if (id == TokenGrammar.WORD) TokenCategory.DocWord else id.category
+                        tokens.add(TokenGrammar.newToken(token, category))
+                        //TODO: Perhaps make all docblock words a DocWord
+                        //tokens.add(TokenGrammar.newToken(token, TokenCategory.DocWord))
+                    }
+                }
+            }
+        }
+        return tokens.toList()
+    }
+}
\ No newline at end of file
diff --git a/docs/src/lexer/HidlLexer.kt b/docs/src/lexer/HidlLexer.kt
new file mode 100644
index 0000000..1bc04fa
--- /dev/null
+++ b/docs/src/lexer/HidlLexer.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package lexer
+
+import java.text.ParseException
+import java.util.*
+
+object HidlLexer : ILexer {
+
+    /**
+     * Given the text of a HIDL file, return a list ot tokens.
+     * Scanner moves forward only, but can check queued tokens.
+     */
+    override fun tokenize(str: String): List<Token> {
+        val tokens = mutableListOf<Token>()
+        //match line to '*/', check for anything after on same line
+        val matchToDocEnd = Regex("""(.*)\s+${Regex.escape(TokenGrammar.DOC_END.value)}(.*)$""")
+
+        //pad delimiter tokens so the scanner picks them up.
+        val paddedStr = ILexer.padDelimiters(str)
+
+        Scanner(paddedStr).use { scanner ->
+            while (scanner.hasNext()) {
+                val token = scanner.next()
+
+                when (token) {
+                    //strip line comments
+                    TokenGrammar.COMMENT_LINE.value -> scanner.nextLine()
+
+                    //strip block comments -- jump to comment close
+                    TokenGrammar.COMMENT_START.value -> {
+                        if (scanner.findWithinHorizon(Regex.escape(TokenGrammar.DOC_END.value), 0) == null) {
+                            throw ParseException("Unable to find closing comment marker", tokens.lastIndex)
+                        }
+                    }
+
+                    //slurp text between /** and */ into a string,
+                    //tokenize string using the doc comment lexer,
+                    //append those tokens with the rest of the file tokens.
+                    TokenGrammar.DOC_START.value -> {
+                        tokens.add(TokenGrammar.newToken(token)) //doc_start
+                        //slurp everything until doc_end into a string. but want to keep newline formatting
+                        val sb = StringBuilder()
+                        while (scanner.hasNextLine()) {
+                            val line = scanner.nextLine()
+
+                            val matches = matchToDocEnd.find(line)?.groups
+                            if (matches != null) {
+                                if (!matches[2]!!.value.isNullOrBlank()) {
+                                    throw ParseException("No text after '*/' on same line: ${line}", 0)
+                                }
+                                //found doc_end
+                                sb.append(matches[1]!!.value)
+                                break
+                            } else {
+                                sb.appendln(line)
+                            }
+                        }
+                        //tokenize comment string and append all
+                        tokens += DocLexer.tokenize(sb.toString())
+                        tokens.add(TokenGrammar.newToken(TokenGrammar.DOC_END.value)) //doc_end
+                    }
+
+                    TokenGrammar.AT.value -> {
+                        tokens.add(TokenGrammar.newToken(token)) //'@'
+                        //determine if part of annotation tag
+                        for (annotation in TokenGrammar.annotations()) {
+                            if (scanner.hasNext(annotation.value)) {
+                                scanner.next() //annotation tag
+                                val annotationArgs = StringBuilder()
+                                //capture any args: (...)
+                                if (scanner.hasNext(Regex.escape(TokenGrammar.PAREN_OPEN.value))) {
+                                    while (!scanner.hasNext(Regex.escape(TokenGrammar.PAREN_CLOSE.value))) {
+                                        annotationArgs.append(scanner.next()).append(" ")
+                                    }
+                                    if (!scanner.hasNext()) {
+                                        throw ParseException("Unable to find closing annotation paren", tokens.lastIndex)
+                                    }
+                                    annotationArgs.append(scanner.next()) //')'
+                                }
+                                //all annotation args are embedded in the token's value
+                                tokens.add(TokenGrammar.newToken(identifier = annotation, value = annotationArgs.toString()))
+                            }
+                        }
+                    }
+
+                    else -> tokens.add(TokenGrammar.newToken(token))
+                }
+            }
+        }
+        return tokens.toList()
+    }
+}
\ No newline at end of file
diff --git a/docs/src/lexer/ILexer.kt b/docs/src/lexer/ILexer.kt
new file mode 100644
index 0000000..368ead7
--- /dev/null
+++ b/docs/src/lexer/ILexer.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package lexer
+
+import java.io.File
+
+interface ILexer {
+    fun tokenize(str: String): List<Token>
+
+    fun tokenize(file: File): List<Token> {
+        return this.tokenize(file.readText())
+    }
+
+    companion object {
+
+        fun padDelimiters(str: String): String {
+            val sb = StringBuilder()
+            val delimiters = TokenGrammar.values()
+                    .filter { it.category == TokenCategory.Delimiter }
+                    .filter { it != TokenGrammar.COMMENT_START } //don't convert '/**' to '/* *'
+                    .map { it.value } //return string representation
+
+            str.lineSequence().forEach { line ->
+                var newLine = line
+                for (token in delimiters) {
+                    newLine = newLine.replace(token, " $token ")
+                }
+                //delimiter corrections
+                newLine = unpadDecimal(newLine) //'nn . nn' => 'n.n'
+                newLine = newLine.replace(":\\s+:".toRegex(), TokenGrammar.PKG_SCOPE.value)  //': :' => '::'
+                //squeeze multi-char ops with chevrons
+                newLine = newLine.replace("<\\s+<".toRegex(), TokenGrammar.LSHIFT.value)
+                newLine = newLine.replace(">\\s+>".toRegex(), TokenGrammar.RSHIFT.value)
+                newLine = newLine.replace("<\\s+=".toRegex(), TokenGrammar.LEQ.value)
+                newLine = newLine.replace(">\\s+=".toRegex(), TokenGrammar.GEQ.value)
+
+                sb.appendln(newLine)
+            }
+            return sb.toString()
+        }
+
+        /**
+         * Replace 'nn . nn' with 'n.n'
+         * Doesn't take into account decimals with missing a prefix or suffix, e.g. '9.' or '.9'
+         */
+        private fun unpadDecimal(str: String): String {
+            var newStr = str
+            Regex("(\\d+)\\s*\\.\\s*(\\d+)").findAll(newStr).forEach { matchResult ->
+                val n1 = matchResult.groups[1]?.value
+                val n2 = matchResult.groups[2]?.value
+                if (n1 != null && n2 != null) {
+                    newStr = newStr.replace("${n1}\\s*\\.\\s*${n2}".toRegex(), "${n1}.${n2}")
+                }
+            }
+            return newStr
+        }
+
+        /**
+         * Clean up the padded and tokenized doc block (reverse padDelimiters)
+         */
+        fun unpadDelimiters(str: String): String {
+            var newStr = str
+            val delimiters = TokenGrammar.values()
+                    .filter { it.category == TokenCategory.Delimiter }
+                    .map { it.value } //return string representation
+
+            for (token in delimiters) {
+                newStr = newStr.replace(" $token ", token)
+            }
+            //special case
+            newStr = newStr.replace(Regex("\\s+\\.\\s*$"), ".") //end-of-line sentence periods
+            newStr = newStr.replace(",", ", ") //give comma some breathing room
+            return newStr
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/docs/src/lexer/Token.kt b/docs/src/lexer/Token.kt
new file mode 100644
index 0000000..3038b77
--- /dev/null
+++ b/docs/src/lexer/Token.kt
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package lexer
+
+data class Token(val identifier: TokenGrammar, val value: String, val category: TokenCategory)
+
+enum class TokenCategory {
+    Annotation, Delimiter, Keyword, Number, Op, TypeDef, Word, DocComment, DocAnnotation, DocWord
+}
+
+enum class TokenGrammar(val value: String, val category: TokenCategory) {
+
+    WORD("", TokenCategory.Word),
+    INTEGER("", TokenCategory.Number),
+    DECIMAL("", TokenCategory.Number),
+
+    AT("@", TokenCategory.Delimiter),
+    EMPTY_LINE("EMPTY_LINE", TokenCategory.Delimiter), //sub in doc string, don't include value in token
+
+    DOC_START("/**", TokenCategory.Delimiter),
+    DOC_END("*/", TokenCategory.Delimiter),
+    COMMENT_LINE("//", TokenCategory.Delimiter),
+    COMMENT_START("/*", TokenCategory.Delimiter),
+
+    // HIDL grammar: {android}/system/tools/hidl/hidl-gen_l.ll
+    ENUM("enum", TokenCategory.TypeDef),
+    EXTENDS("extends", TokenCategory.Keyword),
+    GENERATES("generates", TokenCategory.Keyword),
+    IMPORT("import", TokenCategory.Keyword),
+    INTERFACE("interface", TokenCategory.TypeDef),
+    PACKAGE("package", TokenCategory.Keyword),
+    STRUCT("struct", TokenCategory.TypeDef),
+    TYPEDEF("typedef", TokenCategory.TypeDef),
+    UNION("union", TokenCategory.TypeDef),
+    BITFIELD("bitfield", TokenCategory.TypeDef),
+    VEC("vec", TokenCategory.TypeDef),
+    REF("ref", TokenCategory.TypeDef),
+    ONEWAY("oneway", TokenCategory.Keyword),
+
+    BOOL("bool", TokenCategory.TypeDef),
+    INT8_T("int8_t", TokenCategory.TypeDef),
+    UINT8_T("uint8_t", TokenCategory.TypeDef),
+    INT16_T("int16_t", TokenCategory.TypeDef),
+    UINT16_T("uint16_t", TokenCategory.TypeDef),
+    INT32_T("int32_t", TokenCategory.TypeDef),
+    UINT32_T("uint32_t", TokenCategory.TypeDef),
+    INT64_T("int64_t", TokenCategory.TypeDef),
+    UINT64_T("int8_t", TokenCategory.TypeDef),
+    FLOAT("float", TokenCategory.TypeDef),
+    DOUBLE("double", TokenCategory.TypeDef),
+
+    DEATH_RECIPIENT("death_recipient", TokenCategory.TypeDef),
+    HANDLE("handle", TokenCategory.TypeDef),
+    MEMORY("memory", TokenCategory.TypeDef),
+    POINTER("pointer", TokenCategory.TypeDef),
+    STRING("string", TokenCategory.TypeDef),
+
+    FMQ_SYNC("fmq_sync", TokenCategory.TypeDef),
+    FMQ_UNSYNC("fmq_unsync", TokenCategory.TypeDef),
+
+    PAREN_OPEN("(", TokenCategory.Delimiter),
+    PAREN_CLOSE(")", TokenCategory.Delimiter),
+    CHEVRON_OPEN("<", TokenCategory.Delimiter),
+    CHEVRON_CLOSE(">", TokenCategory.Delimiter),
+    BRACE_OPEN("{", TokenCategory.Delimiter),
+    BRACE_CLOSE("}", TokenCategory.Delimiter),
+    BRACKET_OPEN("[", TokenCategory.Delimiter),
+    BRACKET_CLOSE("]", TokenCategory.Delimiter),
+    COLON(":", TokenCategory.Delimiter),
+    SEMICOLON(";", TokenCategory.Delimiter),
+    COMMA(",", TokenCategory.Delimiter),
+    PERIOD(".", TokenCategory.Delimiter),
+    EQUAL("=", TokenCategory.Op),
+    PLUS("+", TokenCategory.Op),
+    MINUS("-", TokenCategory.Op),
+    MULTIPLY("*", TokenCategory.Op),
+    DIVIDE("/", TokenCategory.Op),
+    MOD("%", TokenCategory.Op),
+    BITWISE_AND("&", TokenCategory.Op),
+    BITWISE_OR("|", TokenCategory.Op),
+    BITWISE_XOR("^", TokenCategory.Op),
+    LSHIFT("<<", TokenCategory.Op),
+    RSHIFT(">>", TokenCategory.Op),
+    LOGICAL_AND("&&", TokenCategory.Op),
+    LOGICAL_OR("||", TokenCategory.Op),
+    NEGATE("!", TokenCategory.Op),
+    COMPLEMENT("~", TokenCategory.Op),
+    LEQ("<=", TokenCategory.Op),
+    GEQ(">=", TokenCategory.Op),
+    EQUALITY("==", TokenCategory.Op),
+    NEQUALITY("!=", TokenCategory.Op),
+    QUESTION("?", TokenCategory.Op),
+
+    PKG_SCOPE("::", TokenCategory.Delimiter),
+
+    // vts tags
+    ENTRY("entry", TokenCategory.Annotation),
+    EXIT("exit", TokenCategory.Annotation),
+    CALLFLOW("callflow", TokenCategory.Annotation),
+    EXPORT("export", TokenCategory.Annotation),
+
+    // javadoc tags. Not all supported in HIDL
+    // http://docs.oracle.com/javase/1.5.0/docs/tooldocs/windows/javadoc.html#javadoctags
+    // https://docs.google.com/document/d/1zHzOtvI9vIADPmI211F_tXQUd-w6lP5u-Y98lvxPvYQ/view#heading=h.aopuaezi1is6
+    AUTHOR("author", TokenCategory.DocAnnotation),
+    CODE("code", TokenCategory.DocAnnotation),
+    DOC_ROOT("docRoot", TokenCategory.DocAnnotation),
+    DEPRECATED("deprecated", TokenCategory.DocAnnotation),
+    EXCEPTION("exception", TokenCategory.DocAnnotation),
+    INHERIT_DOC("inheritDoc", TokenCategory.DocAnnotation),
+    LINK("link", TokenCategory.DocAnnotation),
+    LINK_PLAIN("linkplain", TokenCategory.DocAnnotation),
+    LITERAL("literal", TokenCategory.DocAnnotation),
+    PARAM("param", TokenCategory.DocAnnotation),
+    RETURN("return", TokenCategory.DocAnnotation),
+    SEE("see", TokenCategory.DocAnnotation),
+    SERIAL("serial", TokenCategory.DocAnnotation),
+    SERIAL_DATA("serialData", TokenCategory.DocAnnotation),
+    SERIAL_FIELD("serialField", TokenCategory.DocAnnotation),
+    SINCE("since", TokenCategory.DocAnnotation),
+    THROWS("throws", TokenCategory.DocAnnotation),
+    VALUE("value", TokenCategory.DocAnnotation),
+    VERSION("version", TokenCategory.DocAnnotation);
+
+    companion object {
+        private val map = TokenGrammar.values().associateBy(TokenGrammar::value)
+        private val matchInt = Regex("\\d+")
+        private val matchFloat = Regex("\\d+[.]\\d+")
+
+        fun getFromValue(value: String): TokenGrammar? {
+            return map[value]
+        }
+
+        fun getFromValueOrDefault(value: String): TokenGrammar {
+            return getFromValue(value) ?: when {
+                matchInt.matches(value) -> INTEGER
+                matchFloat.matches(value) -> DECIMAL
+                else -> WORD
+            }
+        }
+
+        fun newToken(value: String): Token {
+            val tokenGrammar = getFromValueOrDefault(value)
+            return Token(tokenGrammar, value, tokenGrammar.category)
+        }
+
+        fun newToken(value: String, category: TokenCategory): Token {
+            val tokenGrammar = getFromValueOrDefault(value)
+            return Token(tokenGrammar, value, category)
+        }
+
+        fun newToken(value: String, identifier: TokenGrammar): Token {
+            return Token(identifier, value, identifier.category)
+        }
+
+        fun annotations(): List<TokenGrammar> {
+            return TokenGrammar.values().filter { it.category == TokenCategory.Annotation }
+        }
+
+        fun docAnnotations(): List<TokenGrammar> {
+            return TokenGrammar.values().filter { it.category == TokenCategory.DocAnnotation }
+        }
+    }
+}
\ No newline at end of file
diff --git a/docs/src/main.kt b/docs/src/main.kt
new file mode 100644
index 0000000..2069d2e
--- /dev/null
+++ b/docs/src/main.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import lexer.HidlLexer
+import lexer.Token
+import parser.LOG_NAME
+import parser.config
+import parser.files.AbstractFileParser
+import parser.files.InterfaceFileParser
+import parser.files.TypesFileParser
+import writer.files.*
+import java.nio.file.Paths
+import java.text.ParseException
+
+fun main(args: Array<String>) {
+    config.parseArgs(args)
+    if (config.verbose) println("$LOG_NAME args: ${config}")
+
+    val indexWriter = IndexFileWriter() //collects parser entries
+
+    /*
+     * parse and write HAL files
+     */
+    for (fp in config.files) {
+        println("$LOG_NAME Parsing input: $fp")
+
+        val tokens = HidlLexer.tokenize(fp)
+        val (parser, writer) = parseAndGetWriter(tokens)
+
+        indexWriter.addEntry(parser)
+
+        try {
+            //since lazily evaluated, check here for parse errors
+            if (writer.writeToFile()) println("$LOG_NAME Wrote file: ${writer.path}")
+
+        } catch (ex: ParseException) {
+            if (config.skipError) {
+                System.err.println("$LOG_NAME Error parsing file, skipping: $fp")
+                continue
+            } else {
+                System.err.println("$LOG_NAME Error parsing file: $fp")
+                throw ex
+            }
+        } finally {
+            if (config.verbose) writer.printInfo()
+        }
+    }
+
+    /*
+     * non-HAL file
+     */
+    if (indexWriter.writeToFile()) println("$LOG_NAME Wrote index: ${indexWriter.path}")
+
+    val cssPath = Paths.get("${config.outDir}/assets/style.css")
+    if (resources.copyToFile("/resources/assets/style.css", cssPath)) {
+        println("$LOG_NAME Copied resource file: $cssPath")
+    }
+}
+
+fun parseAndGetWriter(tokens: List<Token>): Pair<AbstractFileParser, AbstractParserFileWriter> {
+    val parser: AbstractFileParser
+    val writer: AbstractParserFileWriter
+    if (InterfaceFileParser.isInterface(tokens)) {
+        parser = InterfaceFileParser(tokens)
+        writer = InterfaceFileWriter(parser)
+    } else {
+        parser = TypesFileParser(tokens)
+        writer = TypesFileWriter(parser)
+    }
+    return Pair(parser, writer)
+}
\ No newline at end of file
diff --git a/docs/src/parser/config.kt b/docs/src/parser/config.kt
new file mode 100644
index 0000000..125b4ac
--- /dev/null
+++ b/docs/src/parser/config.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser
+
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.Paths
+import kotlin.system.exitProcess
+
+const val LOG_NAME = "[hidl-doc]"
+
+fun printUsage() {
+    println("""
+Usage: hidl-doc [-i path]
+ -i=path  Add input HAL file or directory to parse
+ -o=dir   Output directory of generated HTML [${config.outDir}]
+ -x=path  Exclude file or directory from files to parse
+ -v       Verbose mode, print parsing info
+ -h       Print this help and exit
+ Error modes:
+ -w       Warn on errors instead of exiting
+ -l       Lint. Warn-only and do not generate files
+ -e       Error and exit on warnings
+ -s       Skip files that encounter parse errors
+""".trim())
+}
+
+object config {
+    val files = mutableListOf<File>()
+    var outDir = toPath("~/out/hidl-doc/html")
+    var verbose = false
+    var lintMode = false
+    var warnOnly = false
+    var errorOnly = false
+    var skipError = false
+
+    override fun toString(): String {
+        return """
+verbose: $verbose
+warnOnly: $warnOnly
+errorOnly: $errorOnly
+skipError: $skipError
+outDir: $outDir
+files: $files
+"""
+    }
+
+    private const val HAL_EXTENSION = ".hal"
+
+    fun parseArgs(args: Array<String>) {
+        if (args.isEmpty()) {
+            printUsage()
+            exitProcess(1)
+        }
+
+        val dirPathArgs = mutableListOf<Path>()
+        val filePathArgs = mutableListOf<Path>()
+        val excludedPathArgs = mutableListOf<Path>()
+
+        val iter = args.iterator()
+        var arg: String
+
+        //parse command-line arguments
+        while (iter.hasNext()) {
+            arg = iter.next()
+
+            when (arg) {
+                "-i" -> {
+                    val path = toPath(iter.next())
+                    if (Files.isDirectory(path)) dirPathArgs.add(path) else filePathArgs.add(path)
+                }
+                "-x" -> excludedPathArgs.add(toPath(iter.next()).toAbsolutePath())
+                "-o" -> outDir = toPath(iter.next())
+                "-v" -> verbose = true
+                "-l" -> { lintMode = true; warnOnly = true }
+                "-w" -> warnOnly = true
+                "-e" -> errorOnly = true
+                "-s" -> skipError = true
+                "-h" -> {
+                    printUsage()
+                    exitProcess(0)
+                }
+                else -> {
+                    System.err.println("Unknown option: $arg")
+                    printUsage()
+                    exitProcess(1)
+                }
+            }
+        }
+
+        //collect files (explicitly passed and search directories)
+        val allFiles = mutableListOf<File>()
+
+        //add individual files
+        filePathArgs.filterNot { excludedPathArgs.contains(it.toAbsolutePath()) }
+                .map { it.toFile() }.map { fp ->
+            if (!fp.isFile || !fp.canRead() || !fp.absolutePath.toLowerCase().endsWith(HAL_EXTENSION)) {
+                System.err.println("Error: Invalid $HAL_EXTENSION file: ${fp.path}")
+                exitProcess(1)
+            }
+            fp
+        }.map { allFiles.add(it) }
+
+        //check directory args
+        dirPathArgs.map { it.toFile() }
+                .map { findFiles(it, allFiles, HAL_EXTENSION, excludedPathArgs) }
+
+        //consolidate duplicates
+        allFiles.distinctBy { it.canonicalPath }
+                .forEach { files.add(it) }
+
+        if (files.isEmpty()) {
+            System.err.println("Error: Can't find any $HAL_EXTENSION files")
+            exitProcess(1)
+        }
+    }
+
+    /**
+     * Recursively search for files in a directory matching an extension and add to files.
+     */
+    private fun findFiles(dir: File, files: MutableList<File>, ext: String, excludedPaths: List<Path>) {
+        if (!dir.isDirectory || !dir.canRead()) {
+            System.err.println("Invalid directory: ${dir.path}, aborting")
+            exitProcess(1)
+        }
+        dir.listFiles()
+                .filterNot { excludedPaths.contains(it.toPath().toAbsolutePath()) }
+                .forEach { fp ->
+                    if (fp.isDirectory) {
+                        findFiles(fp, files, ext, excludedPaths)
+                    } else if (fp.absolutePath.toLowerCase().endsWith(ext)) {
+                        files.add(fp)
+                    }
+                }
+    }
+
+    private fun toPath(string: String): Path {
+        //replace shell expansion for HOME
+        return Paths.get(string.replaceFirst("~", System.getProperty("user.home")))
+    }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/AbstractParser.kt b/docs/src/parser/elements/AbstractParser.kt
new file mode 100644
index 0000000..45c1fc2
--- /dev/null
+++ b/docs/src/parser/elements/AbstractParser.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.elements
+
+import lexer.Token
+import lexer.TokenGrammar
+import parser.peekPreviousToken
+import parser.peekToken
+import java.text.ParseException
+
+/**
+ * Start parsing at position in an iterator. Find the end, collect the results.
+ * @param iter An iterator of a list of tokens, starting at a position to parse.
+ */
+abstract class AbstractParser(iter: ListIterator<Token>) {
+
+    val indexStart: Int
+
+    init {
+        while (iter.hasNext() && peekToken(iter)?.identifier == TokenGrammar.EMPTY_LINE) {
+            iter.next() //skip over beginning empty lines
+        }
+        require(iter.hasNext()) { "Iterator is empty" }
+        indexStart = iter.nextIndex()
+    }
+
+    /**
+     * Do something with the tokens.
+     */
+    abstract fun parseTokens(tokens: List<Token>)
+
+    /**
+     * Determine end of token sequence, collect tokens from here to there.
+     */
+    abstract fun scanTokens(iter: ListIterator<Token>): List<Token>
+
+    protected fun scanDocTokens(iter: ListIterator<Token>): List<Token> {
+        val tokens = mutableListOf<Token>()
+        var token: Token
+
+        //ignore any empty lines that start the doc block (if called after doc_start)
+        while (peekPreviousToken(iter)?.identifier == TokenGrammar.EMPTY_LINE) iter.previous()
+
+        //queue up doc_start if called after
+        if (peekPreviousToken(iter)?.identifier == TokenGrammar.DOC_START) iter.previous()
+
+        if (peekToken(iter)!!.identifier != TokenGrammar.DOC_START)
+            throw ParseException("Doc comment blocks must begin with ${TokenGrammar.DOC_START.value}", this.indexStart)
+
+        tokens.add(iter.next()) //doc_start
+
+        while (iter.hasNext()) {
+            token = iter.next()
+            tokens.add(token)
+            if (token.identifier == TokenGrammar.DOC_END) {
+                break
+            } else if (token.identifier == TokenGrammar.DOC_START) {
+                throw ParseException("Nested doc comments not allowed", this.indexStart)
+            }
+        }
+
+        if (peekPreviousToken(iter)?.identifier != TokenGrammar.DOC_END) {
+            throw ParseException("Unable to find doc comment end", this.indexStart)
+        }
+        return tokens
+    }
+
+    /**
+     * Collect annotations (optional) to end of declaration (code body), may be nested.
+     */
+    protected fun scanDeclarationTokens(iter: ListIterator<Token>): List<Token> {
+        val tokens = mutableListOf<Token>()
+        var token: Token
+        var nestLevel = 0
+        var inDoc = false
+
+        while (iter.hasNext()) {
+            token = iter.next()
+            tokens.add(token)
+
+            if (token.identifier == TokenGrammar.DOC_START) {
+                inDoc = true
+            } else if (token.identifier == TokenGrammar.DOC_END) {
+                inDoc = false
+            }
+
+            if (inDoc) {
+                continue
+            } else if (token.identifier == TokenGrammar.BRACE_OPEN) {
+                nestLevel++
+            } else if (token.identifier == TokenGrammar.BRACE_CLOSE) {
+                nestLevel--
+            } else if (token.identifier == TokenGrammar.SEMICOLON && nestLevel == 0) {
+                break
+            }
+        }
+        assert(tokens.last().identifier == TokenGrammar.SEMICOLON)
+        return tokens
+    }
+
+    fun resetIterator(iter: ListIterator<Token>) {
+        while (iter.hasPrevious() && iter.previousIndex() >= indexStart) {
+            iter.previous()
+        }
+        assert(iter.nextIndex() == this.indexStart)
+    }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/AnnotationParser.kt b/docs/src/parser/elements/AnnotationParser.kt
new file mode 100644
index 0000000..a1a32b4
--- /dev/null
+++ b/docs/src/parser/elements/AnnotationParser.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.elements
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.peekPreviousToken
+import parser.peekToken
+import java.text.ParseException
+
+class AnnotationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractParser(iter) {
+
+    lateinit var name: TokenGrammar
+    lateinit var value: String
+
+    init {
+        parseTokens(scanTokens(iter))
+        if (shouldResetIterator) resetIterator(iter)
+    }
+
+    override fun scanTokens(iter: ListIterator<Token>): List<Token> {
+        val tokens = mutableListOf<Token>()
+        //depending how called, queue up annotation
+        if (peekToken(iter)?.identifier == TokenGrammar.AT) iter.next()
+        if (peekPreviousToken(iter)?.category == TokenCategory.Annotation) iter.previous()
+
+        if (peekToken(iter)!!.category != TokenCategory.Annotation)
+            throw ParseException("Doc token sequence must begin with an annotation", this.indexStart)
+
+        //just one token, info embedded
+        tokens.add(iter.next())
+        return tokens
+    }
+
+    override fun parseTokens(tokens: List<Token>) {
+        val iter = tokens.listIterator()
+        assert(peekToken(iter)!!.category == TokenCategory.Annotation)
+        var token = iter.next()
+
+        this.name = token.identifier
+        this.value = parseAnnotationValue(token)
+    }
+
+    //capture text between parens
+    private fun parseAnnotationValue(token: Token): String {
+        return Regex(""".*\((.*)\).*""")
+                .matchEntire(token.value)
+                ?.groups?.get(1)
+                ?.value?.trim()
+                ?: ""
+    }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/DocAnnotationParser.kt b/docs/src/parser/elements/DocAnnotationParser.kt
new file mode 100644
index 0000000..ee043da
--- /dev/null
+++ b/docs/src/parser/elements/DocAnnotationParser.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.elements
+
+import lexer.ILexer
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.peekPreviousToken
+import parser.peekToken
+import java.text.ParseException
+
+
+class DocAnnotationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractParser(iter) {
+
+    lateinit var tag: TokenGrammar
+    var arg: String? = null //some tags have arguments (eg. @param arg desc)
+    lateinit var description: String
+
+    init {
+        parseTokens(scanTokens(iter))
+        if (shouldResetIterator) resetIterator(iter)
+    }
+
+    private fun formatValue(tokens: List<Token>): String {
+        return if (tokens.isEmpty()) {
+            ""
+        } else {
+            tokens.map {
+                when (it.identifier) {
+                    TokenGrammar.EMPTY_LINE -> "\n\n"
+                    else -> it.value
+                }
+            }
+                    .joinToString(" ")
+                    .let { ILexer.unpadDelimiters(it) }
+        }
+    }
+
+    /**
+     * Scan until: doc end token, empty line, another @param
+     */
+    override fun scanTokens(iter: ListIterator<Token>): List<Token> {
+        val tokens = mutableListOf<Token>()
+        var token: Token
+
+        //depending how invoked, queue up doc annotation token
+        if (peekToken(iter)?.identifier == TokenGrammar.AT) iter.next()
+        if (peekPreviousToken(iter)?.category == TokenCategory.DocAnnotation) iter.previous()
+
+        if (peekToken(iter)!!.category != TokenCategory.DocAnnotation)
+            throw ParseException("Token sequence must begin with a DocAnnotation", this.indexStart)
+
+        loop@ while (iter.hasNext()) {
+            token = iter.next()
+
+            when {
+                //descriptions don't span blank lines
+                token.identifier == TokenGrammar.EMPTY_LINE -> break@loop
+
+                //if doc block ends or found next annotation tag, back up and bail
+                token.identifier == TokenGrammar.DOC_END ||
+                token.identifier == TokenGrammar.AT && peekToken(iter)?.category == TokenCategory.DocAnnotation -> {
+                    iter.previous()
+                    break@loop
+                }
+
+                else -> tokens.add(token)
+            }
+        }
+        return tokens
+    }
+
+    override fun parseTokens(tokens: List<Token>) {
+        val iter = tokens.listIterator()
+        assert(iter.hasNext())
+        val token = iter.next()
+
+        if (token.category != TokenCategory.DocAnnotation)
+            throw ParseException("Invalid doc anootation tag: ${token.value}", this.indexStart)
+
+        //annotation tag name (must be in TokenGrammar)
+        this.tag = token.identifier
+
+        //annotation tag can take an optional argument
+        //so can return (preferred). TODO: check HALs if mandatory
+        if (token.identifier == TokenGrammar.PARAM || token.identifier == TokenGrammar.RETURN) {
+            if (iter.hasNext()) this.arg = iter.next().value
+        }
+
+        //the rest is annotation description
+        val descTokens = mutableListOf<Token>()
+        while (iter.hasNext()) {
+            descTokens.add(iter.next())
+        }
+
+        this.description = if (descTokens.isEmpty()) {
+            ""
+        } else {
+            descTokens.map { if (it.identifier == TokenGrammar.EMPTY_LINE) "\n\n" else it.value }
+                    .joinToString(" ")
+                    .let { ILexer.unpadDelimiters(it) }
+        }
+    }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/DocParser.kt b/docs/src/parser/elements/DocParser.kt
new file mode 100644
index 0000000..fd1f8a1
--- /dev/null
+++ b/docs/src/parser/elements/DocParser.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.elements
+
+import lexer.ILexer
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.peekToken
+
+class DocParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractParser(iter) {
+
+    val description: String by lazy { formatDescription(this.descTokens) }
+    var docAnnotationParsers = mutableListOf<DocAnnotationParser>()
+
+    private var descTokens = mutableListOf<Token>()
+
+    init {
+        parseTokens(scanTokens(iter))
+        if (shouldResetIterator) resetIterator(iter)
+    }
+
+    private fun formatDescription(tokens: List<Token>): String {
+        return tokens
+                .filterNot { it.identifier == TokenGrammar.DOC_START }
+                .filterNot { it.identifier == TokenGrammar.DOC_END }
+                .map {
+                    when (it.identifier) {
+                        TokenGrammar.EMPTY_LINE -> "\n\n"
+                        else -> it.value
+                    }
+                }
+                .joinToString(" ")
+                .let { ILexer.unpadDelimiters(it) }
+    }
+
+    override fun scanTokens(iter: ListIterator<Token>): List<Token> {
+        //keep doc_start and doc_end tokens /** ... */
+        return scanDocTokens(iter)
+    }
+
+    override fun parseTokens(tokens: List<Token>) {
+        val iter = tokens.listIterator()
+        var token = iter.next() //doc_start
+
+        assert(token.identifier == TokenGrammar.DOC_START)
+        assert(tokens.last().identifier == TokenGrammar.DOC_END)
+
+        loop@ while(iter.hasNext()) {
+            token = iter.next()
+
+            when {
+                token.identifier == TokenGrammar.AT && peekToken(iter)?.category == TokenCategory.DocAnnotation -> {
+                    docAnnotationParsers.add(DocAnnotationParser(iter))  //increments iterator
+                }
+                token.identifier == TokenGrammar.DOC_END -> break@loop
+                else -> this.descTokens.add(token)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/EntryCollectionParser.kt b/docs/src/parser/elements/EntryCollectionParser.kt
new file mode 100644
index 0000000..493998c
--- /dev/null
+++ b/docs/src/parser/elements/EntryCollectionParser.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.elements
+
+import lexer.Token
+import lexer.TokenGrammar
+import parser.config
+import writer.warn
+import java.text.ParseException
+
+/**
+ * Collection of doc entryParsers in a token stream.
+ * Typically, like entries are grouped together in a file (eg. Interface def),
+ * But could be useful for declarations with fields (enums, structs).
+ */
+class EntryCollectionParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractParser(iter) {
+
+    var entryParsers = mutableListOf<EntryParser>()
+
+    constructor(tokens: List<Token>) : this(tokens.listIterator())
+
+    init {
+        parseTokens(scanTokens(iter))
+        if (shouldResetIterator) this.resetIterator(iter)
+    }
+
+    //use all the tokens
+    override fun scanTokens(iter: ListIterator<Token>): List<Token> {
+        val tokens = mutableListOf<Token>()
+        while (iter.hasNext()) {
+            tokens.add(iter.next())
+        }
+        return tokens
+    }
+
+    override fun parseTokens(tokens: List<Token>) {
+        val iter = tokens.listIterator()
+        var token: Token
+
+        //find entry parsers - must start with doc_start
+        while(iter.hasNext()) {
+            token = iter.next()
+
+            if (token.identifier == TokenGrammar.DOC_START) {
+                try {
+                    entryParsers.add(EntryParser(iter)) //advances iterator past declaration
+                } catch (ex: IllegalEntryException) {
+                    if (config.warnOnly) {
+                        //bail on current entry but continue
+                        warn("${ex.message}, skipping entry")
+                    } else {
+                        throw ParseException(ex.message, this.indexStart)
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/EntryParser.kt b/docs/src/parser/elements/EntryParser.kt
new file mode 100644
index 0000000..30ffc3b
--- /dev/null
+++ b/docs/src/parser/elements/EntryParser.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.elements
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.config
+import parser.elements.declarations.*
+import parser.peekPreviousToken
+import parser.peekToken
+import writer.tokenValues
+import writer.warn
+import java.text.ParseException
+
+//an entry consists of: doc block, annotationParsers, declarationParser (code sig)
+//EntryParser contains:
+//- docParser
+//  * description
+//  * docAnnotationParsers
+//    - tag (token)
+//    - arg?
+//    - description
+//- annotationParsers
+//  * name
+//  * value
+//- declarationParser
+//  * name
+//  * members [CompoundDeclarationParser]: type, name, tokens, typedef [CompoundMemberDeclaration]
+//  * members [EnumDeclarationParser]: name, value?
+//  * type [EnumDeclarationParser]
+//  * type [TypedefDeclarationParser]
+//  * extendsName [InterfaceDeclarationParser]
+//  * extendsVersion [InterfaceDeclarationParser]
+//  * prefix [MethodDeclarationParser]
+//  * params [MethodDeclarationParser]: ArgEntry: type, name
+//  * returns [[MethodDeclarationParser]: ArgEntry: type, name
+
+class IllegalEntryException(msg: String? = null, cause: Throwable? = null) : Exception(msg, cause)
+
+/**
+ * An entry inclused the doc comment block, code docAnnotationParsers, and method signature/interface
+ */
+class EntryParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractParser(iter) {
+
+    //doc description, summary, and doc annottions
+    lateinit var docParser: DocParser
+    //annotation
+    val annotationParsers = mutableListOf<AnnotationParser>()
+    //declaration - properties depend on declarationParser subclass
+    lateinit var declarationParser: AbstractDeclarationParser
+
+    init {
+        parseTokens(scanTokens(iter))
+        if (shouldResetIterator) resetIterator(iter)
+    }
+
+    //start at doc_start, collect until end of declaration
+    override fun scanTokens(iter: ListIterator<Token>): List<Token> {
+        //queue up doc_start
+        //ignore any empty lines that start the doc block
+        while (peekPreviousToken(iter)?.identifier == TokenGrammar.EMPTY_LINE) iter.previous()
+        //if called after the doc_start was found
+        if (peekPreviousToken(iter)?.identifier == TokenGrammar.DOC_START) iter.previous()
+
+        val tokens = mutableListOf<Token>()
+        //collect doc block /** ... */
+        tokens += scanDocTokens(iter)
+        //collect annotations and declaration, nested to semicolon
+        tokens += scanDeclarationTokens(iter)
+        return tokens
+    }
+
+    override fun parseTokens(tokens: List<Token>) {
+        val iter = tokens.listIterator()
+
+        /*
+         * doc comment block
+         */
+        do {
+            assert(peekToken(iter)!!.identifier == TokenGrammar.DOC_START)
+            this.docParser = DocParser(iter) //increments iterator
+            assert(peekPreviousToken(iter)!!.identifier == TokenGrammar.DOC_END)
+
+            //if there's consecutive doc blocks, use the last one found in warning mode, otherwise error
+            if (peekToken(iter)?.identifier != TokenGrammar.DOC_START) {
+                break //good to go
+            } else {
+                val msg = "Found consecutive doc block after: ${this.docParser.description}"
+                if (config.warnOnly) {
+                    warn("${msg}\nUsing last found doc block.")
+                } else {
+                    throw ParseException(msg, this.indexStart)
+                }
+            }
+        } while (true)
+
+        /*
+         * annotations (optional)
+         */
+        while (iter.hasNext() && peekToken(iter)!!.identifier == TokenGrammar.AT) {
+            iter.next()
+            if (peekToken(iter)?.category == TokenCategory.Annotation) {
+                this.annotationParsers.add(AnnotationParser(iter)) //increments iterator
+            } else {
+                throw ParseException("Unknown annotation tag: ${peekToken(iter)?.value}", this.indexStart)
+            }
+        }
+
+        /*
+         * declaration
+         */
+        val token = peekToken(iter) ?: throw ParseException("No declaration body available", this.indexStart)
+
+        //check we're not at an annotation
+        assert(token.identifier != TokenGrammar.AT && token.category != TokenCategory.Annotation)
+
+        /*
+         * known bad starts for a declaration
+         */
+
+        if (token.identifier == TokenGrammar.DOC_START) {
+            throw ParseException("Bad doc block location:\n${tokenValues(tokens)}", this.indexStart)
+        } else if (token.identifier == TokenGrammar.PACKAGE) {
+            //usually this means they've used a doc block for the license
+            throw IllegalEntryException("Don't document the package declaration") //handled in EntryCollectionParser
+        } else if (token.category != TokenCategory.Word
+                && token.category != TokenCategory.TypeDef
+                && token.category != TokenCategory.Keyword) {
+            //sanity check - skip entry or bail
+            throw IllegalEntryException("Invalid start for entry declaration: '${token.value}'\n" +
+                    "tokens: ${tokenValues(tokens)}")
+            //throw ParseException("Invalid start for entry declaration: ${token}\ntoken: ${token.value}\n${tokenValues(tokens)}", this.indexStart)
+        }
+
+        this.declarationParser = when (token.identifier) {
+            TokenGrammar.INTERFACE -> {
+                this.shouldResetIterator = true
+                InterfaceDeclarationParser(iter)
+            }
+            TokenGrammar.ENUM -> EnumDeclarationParser(iter)
+            TokenGrammar.TYPEDEF -> TypedefDeclarationParser(iter)
+            TokenGrammar.STRUCT, TokenGrammar.UNION -> CompoundDeclarationParser(iter)
+            else -> MethodDeclarationParser(iter)
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/declarations/AbstractDeclarationParser.kt b/docs/src/parser/elements/declarations/AbstractDeclarationParser.kt
new file mode 100644
index 0000000..6d906a5
--- /dev/null
+++ b/docs/src/parser/elements/declarations/AbstractDeclarationParser.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.elements.declarations
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.elements.AbstractParser
+import parser.peekPreviousToken
+import parser.peekToken
+import java.text.ParseException
+
+//can be a method, struct, enum, typedef
+abstract class AbstractDeclarationParser(iter: ListIterator<Token>) : AbstractParser(iter) {
+
+    abstract var name: String //user-specified name declarationParser
+
+    //should be segmented in EntryParser, remaining tokens are the declarationParser, may be nested
+    override fun scanTokens(iter: ListIterator<Token>): List<Token> {
+        val token = peekPreviousToken(iter) ?: throw ParseException("No token before declaration", this.indexStart)
+
+        if (token.category != TokenCategory.Annotation && token.identifier != TokenGrammar.DOC_END)
+            throw ParseException("Invalid declaration start", this.indexStart)
+
+        return scanDeclarationTokens(iter)
+    }
+
+    /**
+     * Takes a delimited separated list and splits entries into list of list<token>.
+     * Ignore nested lists using same open/close delimiter.
+     * For method param lists, enum members, struct members, etc.
+     */
+    protected fun scanDelimitedList(iter: ListIterator<Token>,
+                                    delimiter: TokenGrammar = TokenGrammar.COMMA,
+                                    openDelimiter: TokenGrammar = TokenGrammar.PAREN_OPEN,
+                                    closeDelimiter: TokenGrammar = TokenGrammar.PAREN_CLOSE): List<List<Token>> {
+        val allFields = mutableListOf<List<Token>>() //top-level list
+        //queue up list open
+        if (iter.hasPrevious() && peekPreviousToken(iter)!!.identifier == openDelimiter) {
+            iter.previous()
+        }
+        var token = iter.next()
+        if (token.identifier != openDelimiter)
+            throw ParseException("Expected list start '${openDelimiter}', but got '${token.identifier}'", this.indexStart)
+
+        // collect tokens between open/close delimiters, fields separated by delimiter.
+        // ignore if nested, ignore in doc comment
+        while (iter.hasNext()) {
+            token = peekToken(iter)!! //iter.next()
+
+            if (token.identifier == closeDelimiter) {
+                iter.next()
+                break
+            } else if (token.identifier == delimiter) {
+                iter.next()
+                continue //skip
+            } else {
+                //start field entry
+                val fieldTokens = mutableListOf<Token>()
+                var inDoc = false
+                var nestLevel = 0
+
+                while (iter.hasNext()) {
+                    token = iter.next()
+
+                    if (token.identifier == TokenGrammar.DOC_START) {
+                        inDoc = true
+                    } else if (token.identifier == TokenGrammar.DOC_END) {
+                        inDoc = false
+                    }
+
+                    //check for end of field
+                    if ((token.identifier == delimiter || token.identifier == closeDelimiter) && nestLevel == 0 && !inDoc) {
+                        break
+                    } else {
+                        fieldTokens.add(token)
+                    }
+
+                    if (token.identifier == openDelimiter) {
+                        nestLevel++
+                    } else if (token.identifier == closeDelimiter && nestLevel > 0) {
+                        nestLevel--
+                    }
+                }
+
+                //add entry
+                allFields.add(fieldTokens)
+                //check for end of list
+                if (token.identifier == closeDelimiter && nestLevel == 0) {
+                    break
+                }
+            }
+        }
+
+        if (!iter.hasPrevious() || peekPreviousToken(iter)!!.identifier != closeDelimiter) {
+            throw ParseException("Didn't find closing '${closeDelimiter.value}' for list, found '${peekPreviousToken(iter)!!.value}'", this.indexStart)
+        }
+        return allFields
+    }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/declarations/CompoundDeclarationParser.kt b/docs/src/parser/elements/declarations/CompoundDeclarationParser.kt
new file mode 100644
index 0000000..69f82d1
--- /dev/null
+++ b/docs/src/parser/elements/declarations/CompoundDeclarationParser.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.elements.declarations
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.elements.DocParser
+import writer.tokenValues
+import java.text.ParseException
+
+/**
+ * Used for Structs and Unions
+ */
+class CompoundDeclarationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractDeclarationParser(iter) {
+    lateinit var type: TokenGrammar
+    lateinit override var name: String
+    var members = mutableListOf<IMemberDeclaration>() //defined below
+
+    init {
+        parseTokens(scanTokens(iter))
+        if (shouldResetIterator) resetIterator(iter)
+    }
+
+    override fun parseTokens(tokens: List<Token>) {
+        val iter = tokens.listIterator()
+        var token = iter.next()
+        assert(token.identifier == TokenGrammar.STRUCT || token.identifier == TokenGrammar.UNION)
+        assert(tokens.last().identifier == TokenGrammar.SEMICOLON)
+
+        //type - struct or union
+        this.type = token.identifier
+
+        //name
+        token = iter.next()
+        if (token.category != TokenCategory.Word)
+            throw ParseException("Invalid struct name: ${tokenValues(tokens)}", this.indexStart)
+        this.name = token.value
+
+        //parse each semicolon-delimited statement
+        scanDelimitedList(iter,
+                delimiter = TokenGrammar.SEMICOLON,
+                openDelimiter = TokenGrammar.BRACE_OPEN,
+                closeDelimiter = TokenGrammar.BRACE_CLOSE)
+                .forEach {
+                    var docParser: DocParser? = null
+                    var statementTokens = it.toMutableList()
+                    if (statementTokens.isEmpty())
+                        throw ParseException("Invalid statement in: ${tokenValues(tokens)}", this.indexStart)
+
+                    //If doc, extract doc tokens and parse, and remove from statement tokens
+                    if (statementTokens.first().identifier == TokenGrammar.DOC_START) {
+                        val idx = statementTokens.indexOfFirst { it.identifier == TokenGrammar.DOC_END }
+                        if (idx == -1) throw ParseException("Unable to find doc_end", this.indexStart)
+                        val docTokens = statementTokens.subList(0, idx+1)
+                        docParser = DocParser(docTokens.listIterator())
+                        statementTokens = statementTokens.subList(idx+1, statementTokens.size)
+                    }
+
+                    if (statementTokens.isEmpty())
+                        throw ParseException("Invalid statement in: ${tokenValues(tokens)}", this.indexStart)
+
+                    when(statementTokens.first().identifier) {
+                        TokenGrammar.STRUCT, TokenGrammar.UNION -> {
+                            assert(statementTokens.first().category == TokenCategory.TypeDef)
+                            this.members.add(CompoundMemberDeclaration(
+                                    typeDef = statementTokens.first().identifier,
+                                    type = statementTokens.get(1).value,
+                                    name = statementTokens.last().value,
+                                    docParser = docParser,
+                                    tokens = statementTokens.subList(2, statementTokens.size-1)
+                            ))
+                        }
+                        TokenGrammar.ENUM -> {
+                            assert(statementTokens.size > 1)
+                            this.members.add(MemberDeclaration(
+                                    type = statementTokens.first().value,
+                                    name = statementTokens.get(1).value,
+                                    docParser = docParser,
+                                    tokens = statementTokens
+                            ))
+                        }
+                        else -> {
+                            this.members.add(MemberDeclaration(
+                                    type = statementTokens.first().value,
+                                    name = statementTokens.last().value,
+                                    docParser = docParser,
+                                    tokens = statementTokens
+                            ))
+                        }
+                    }
+                }
+    }
+}
+
+interface IMemberDeclaration {
+    val type: String
+    val name: String
+    val docParser: DocParser?
+    val tokens: List<Token> //TODO: doesn't seem needed
+}
+
+class MemberDeclaration(override val type: String,
+                        override val name: String,
+                        override val docParser: DocParser?,
+                        override val tokens: List<Token>) : IMemberDeclaration
+
+class CompoundMemberDeclaration(override val type: String,
+                                override val name: String,
+                                override val docParser: DocParser?,
+                                override val tokens: List<Token>,
+                                val typeDef: TokenGrammar) : IMemberDeclaration
diff --git a/docs/src/parser/elements/declarations/EnumDeclarationParser.kt b/docs/src/parser/elements/declarations/EnumDeclarationParser.kt
new file mode 100644
index 0000000..fb0fe64
--- /dev/null
+++ b/docs/src/parser/elements/declarations/EnumDeclarationParser.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.elements.declarations
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.elements.DocParser
+import parser.peekToken
+import writer.tokenValues
+import java.text.ParseException
+
+class EnumDeclarationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractDeclarationParser(iter) {
+
+    lateinit override var name: String
+    lateinit var type: String
+    var members = mutableListOf<EnumMember>()
+
+    init {
+        parseTokens(scanTokens(iter))
+        if (shouldResetIterator) resetIterator(iter)
+    }
+
+    override fun parseTokens(tokens: List<Token>) {
+        val iter = tokens.listIterator()
+        var token = iter.next()
+        assert(token.identifier == TokenGrammar.ENUM)
+        assert(tokens.last().identifier == TokenGrammar.SEMICOLON)
+
+        //name
+        token = iter.next()
+        if (token.category != TokenCategory.Word)
+            throw ParseException("Invalid enum name: ${tokenValues(tokens)}", this.indexStart)
+        this.name = token.value
+
+        token = iter.next() //':'
+        if (token.identifier != TokenGrammar.COLON)
+            throw ParseException("Invalid enum type syntax: ${tokenValues(tokens)}", this.indexStart)
+
+        //type: can be type name or package
+        assert(iter.hasNext())
+        val sb = StringBuilder()
+        while (iter.hasNext() && peekToken(iter)!!.identifier != TokenGrammar.BRACE_OPEN) {
+            sb.append(iter.next().value)
+        }
+        this.type = sb.toString()
+
+        //members
+        //convert iterator sequence of comma separated tokens to list of lists
+        scanDelimitedList(iter, openDelimiter = TokenGrammar.BRACE_OPEN, closeDelimiter = TokenGrammar.BRACE_CLOSE)
+                .forEach {
+                    var statementTokens = it.toMutableList()
+                    var docParser: DocParser? = null
+                    assert(statementTokens.isNotEmpty())
+
+                    //If doc, extract doc tokens and parse, and remove from statement tokens
+                    if (statementTokens.first().identifier == TokenGrammar.DOC_START) {
+                        val idx = statementTokens.indexOfFirst { it.identifier == TokenGrammar.DOC_END }
+                        if (idx == -1) throw ParseException("Unable to find doc_end", this.indexStart)
+
+                        val docTokens = statementTokens.subList(0, idx+1)
+                        docParser = DocParser(docTokens.listIterator())
+                        statementTokens = statementTokens.subList(idx+1, statementTokens.size)
+                    }
+                    if (statementTokens.isEmpty())
+                        throw ParseException("Invalid member in enum: ${tokenValues(tokens)}", this.indexStart)
+                    val member = EnumMember(statementTokens)
+                    member.docParser = docParser
+                    this.members.add(member)
+                }
+    }
+}
+
+//split member: name [= value]
+class EnumMember(tokens: List<Token>) {
+    val name: String
+    var value: String? = null
+    var docParser: DocParser? = null
+
+    init {
+        assert(tokens.isNotEmpty())
+        this.name = tokens.first().value
+
+        //check for assignment, take right side
+        if (tokens.any { it.identifier == TokenGrammar.EQUAL }) {
+            this.value = tokens.takeLastWhile { it.identifier != TokenGrammar.EQUAL }
+                    .map { it.value }
+                    .joinToString(" ")
+        }
+    }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/declarations/InterfaceDeclarationParser.kt b/docs/src/parser/elements/declarations/InterfaceDeclarationParser.kt
new file mode 100644
index 0000000..ac22c16
--- /dev/null
+++ b/docs/src/parser/elements/declarations/InterfaceDeclarationParser.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.elements.declarations
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+
+class InterfaceDeclarationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractDeclarationParser(iter) {
+
+    lateinit override var name: String
+    var extendsName: String? = null
+    var extendsVersion: Float? = null
+
+    init {
+        parseTokens(scanTokens(iter))
+        if (shouldResetIterator) resetIterator(iter)
+    }
+
+    // example format: interface ITunerCallback extends @1.0::ITunerCallback
+    override fun parseTokens(tokens: List<Token>) {
+        assert(tokens.isNotEmpty())
+        assert(tokens.first().identifier == TokenGrammar.INTERFACE)
+        assert(tokens.last().identifier == TokenGrammar.SEMICOLON)
+
+        //grab first line of declarationParser
+        val sigToks = tokens.takeWhile { it.identifier != TokenGrammar.BRACE_OPEN }
+        assert(sigToks[1].category == TokenCategory.Word)
+        assert(sigToks.last().category == TokenCategory.Word) //either interface name or extends name
+
+        this.name = sigToks[1].value
+
+        //parse extends info (if exists)
+        if (sigToks.any { it.identifier == TokenGrammar.EXTENDS }) {
+            this.extendsName = sigToks.last().value
+            this.extendsVersion = sigToks.find { it.category == TokenCategory.Number }?.value?.toFloat()
+        }
+    }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/declarations/MethodDeclarationParser.kt b/docs/src/parser/elements/declarations/MethodDeclarationParser.kt
new file mode 100644
index 0000000..beb80a7
--- /dev/null
+++ b/docs/src/parser/elements/declarations/MethodDeclarationParser.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.elements.declarations
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import writer.tokenValues
+import java.text.ParseException
+
+data class ArgEntry(val type: String, val name: String)
+
+class MethodDeclarationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractDeclarationParser(iter) {
+    lateinit override var name: String
+
+    var prefix: String? = null
+    var params = mutableListOf<ArgEntry>()
+    var returns = mutableListOf<ArgEntry>()
+
+    init {
+        parseTokens(scanTokens(iter))
+        if (shouldResetIterator) resetIterator(iter)
+    }
+
+    // [prefix] <name> ( [typedef] [name], ...) [generates] ( [typedef], [name], ...);
+    override fun parseTokens(tokens: List<Token>) {
+        assert(tokens.last().identifier == TokenGrammar.SEMICOLON)
+        val iter = tokens.listIterator()
+        var token = iter.next()
+
+        //grab prefix (typedef or keyword), if there
+        if (token.category == TokenCategory.TypeDef || token.category == TokenCategory.Keyword) {
+            this.prefix = token.value
+            token = iter.next()
+        }
+
+        //name
+        if (token.category != TokenCategory.Word) {
+            throw ParseException("Invalid declarationParser name: '${token.value}'\nBad Tokens: ${tokenValues(tokens)}",
+                    this.indexStart)
+        }
+        this.name = token.value
+
+        assert(iter.hasNext())
+        token = iter.next()
+
+        //parse arg list
+        if (token.identifier == TokenGrammar.PAREN_OPEN) {
+            addArgs(iter, this.params)
+            assert(iter.hasNext())
+            token = iter.next()
+        }
+
+        //parse return list
+        if (token.identifier == TokenGrammar.GENERATES) {
+            addArgs(iter, this.returns)
+            assert(iter.hasNext())
+            token = iter.next()
+        }
+
+        assert(token.identifier == TokenGrammar.SEMICOLON)
+    }
+
+    /**
+     * Arguments are provided in the form: (type1 name1, type2 name2, ...)
+     */
+    private fun addArgs(iter: ListIterator<Token>, list: MutableList<ArgEntry>) {
+        val argLists = this.scanDelimitedList(iter) //return list of list<token>
+        for (argList in argLists) {
+            list.add(formatArgEntry(argList)) //use tokens to create ArgEntry
+        }
+    }
+
+    private fun formatArgEntry(paramTokens: List<Token>): ArgEntry {
+        val type = paramTokens.takeWhile { it != paramTokens.last() } //but-last
+                .map { it.value }
+                .joinToString("")
+        val name = paramTokens.last().value
+        return ArgEntry(type, name)
+    }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/declarations/TypedefDeclarationParser.kt b/docs/src/parser/elements/declarations/TypedefDeclarationParser.kt
new file mode 100644
index 0000000..fa3574b
--- /dev/null
+++ b/docs/src/parser/elements/declarations/TypedefDeclarationParser.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.elements.declarations
+
+import lexer.Token
+import lexer.TokenGrammar
+
+class TypedefDeclarationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractDeclarationParser(iter) {
+    lateinit override var name: String //synonym
+    lateinit var type: String          //type declarationParser
+
+    init {
+        parseTokens(scanTokens(iter))
+        if (shouldResetIterator) resetIterator(iter)
+    }
+
+    override fun parseTokens(tokens: List<Token>) {
+        assert(tokens.isNotEmpty())
+        assert(tokens.first().identifier == TokenGrammar.TYPEDEF)
+        assert(tokens.last().identifier == TokenGrammar.SEMICOLON)
+
+        this.name = tokens.get(tokens.size - 2).value
+        this.type = tokens.subList(1, tokens.size - 2).map { it.value }.joinToString("")
+    }
+}
\ No newline at end of file
diff --git a/docs/src/parser/files/AbstractFileParser.kt b/docs/src/parser/files/AbstractFileParser.kt
new file mode 100644
index 0000000..5348dfd
--- /dev/null
+++ b/docs/src/parser/files/AbstractFileParser.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.files
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.elements.EntryCollectionParser
+import parser.elements.EntryParser
+import parser.elements.declarations.CompoundDeclarationParser
+import parser.elements.declarations.EnumDeclarationParser
+import parser.elements.declarations.TypedefDeclarationParser
+import parser.peekPreviousToken
+import parser.peekToken
+import java.text.ParseException
+
+/**
+ * Parses package info and all entries (determined by doc start/end tokens).
+ * Adds empty doc tokens for required types.
+ * This class shouldn't be instantiated on it's own.
+ */
+abstract class AbstractFileParser(tokens: List<Token>) {
+
+    private val packageInfo: PackageInfo by lazy { parsePackageInfo(tokens) }
+
+    abstract val name: String
+    val packageName: String get() = packageInfo.name
+    val packageVersion: Float get() = packageInfo.version
+
+    protected val entries: List<EntryParser> by lazy {
+        assert(tokens.isNotEmpty())
+        //add empty docblocks
+        EntryCollectionParser(insertDocsForRequiredTypes(tokens)).entryParsers
+    }
+
+    val enums: List<EntryParser> by lazy { getEntriesByDeclarationParser<EnumDeclarationParser>() }
+    val typedefs: List<EntryParser> by lazy { getEntriesByDeclarationParser<TypedefDeclarationParser>() }
+    val structs: List<EntryParser> by lazy { getEntriesByCompoundDeclarationParser(TokenGrammar.STRUCT) }
+    val unions: List<EntryParser> by lazy { getEntriesByCompoundDeclarationParser(TokenGrammar.UNION) }
+
+    protected inline fun <reified T> getEntriesByDeclarationParser(): List<EntryParser> {
+        return entries.filter { it.declarationParser is T }
+    }
+
+    private fun getEntriesByCompoundDeclarationParser(identifier: TokenGrammar): List<EntryParser> {
+        return getEntriesByDeclarationParser<CompoundDeclarationParser>()
+                .filter { (it.declarationParser as CompoundDeclarationParser).type == identifier }
+    }
+
+    private val REQUIRED_DOC_TYPES = listOf(
+            TokenGrammar.INTERFACE,
+            TokenGrammar.ENUM,
+            TokenGrammar.STRUCT,
+            TokenGrammar.UNION,
+            TokenGrammar.TYPEDEF)
+
+    /**
+     * Insert doc block before the undocumented types we want to show up.
+     */
+    private fun insertDocsForRequiredTypes(tokens: List<Token>): List<Token> {
+        val tokensCopy = mutableListOf<Token>()
+        val iter = tokens.listIterator()
+        var token: Token
+        var inDoc = false
+
+        while (iter.hasNext()) {
+            token = iter.next()
+            tokensCopy.add(token)
+
+            if (token.identifier == TokenGrammar.DOC_START) {
+                inDoc = true
+                continue
+            } else if (token.identifier == TokenGrammar.DOC_END) {
+                inDoc = false
+                continue
+
+            } else if (!inDoc && token.identifier in REQUIRED_DOC_TYPES) {
+                //make sure it's not a reference to a Generic: <name>
+                if (peekToken(iter)?.identifier == TokenGrammar.CHEVRON_CLOSE) {
+                    continue
+                }
+
+                val idx = indexInsertionPointforDocTokens(tokensCopy)
+                if (idx != -1) {
+                    val removedTokens = mutableListOf<Token>()
+                    repeat(idx) {
+                        removedTokens.add(tokensCopy.removeAt(tokensCopy.size-1))
+                    }
+                    tokensCopy.add(TokenGrammar.newToken(TokenGrammar.DOC_START.value))
+                    tokensCopy.add(TokenGrammar.newToken(TokenGrammar.DOC_END.value))
+                    removedTokens.reversed().forEach { tokensCopy.add(it) }
+                }
+            }
+        }
+        return tokensCopy.toList()
+    }
+
+    /**
+     * @return -1 if documented, otherwise the index count backwards for where
+     *          to begin insertion of doc tokens.
+     */
+    private fun indexInsertionPointforDocTokens(tokens: List<Token>): Int {
+        val iter = tokens.reversed().listIterator()
+        var token: Token
+        var idx = 0
+
+        iter.next() //skip keyword token
+        while (iter.hasNext()) {
+            token = iter.next()
+            if (token.identifier == TokenGrammar.AT || token.category == TokenCategory.Annotation) {
+                idx++
+                continue //skip annotations
+            } else {
+                return if (token.identifier == TokenGrammar.DOC_END) -1 else idx+1
+            }
+        }
+        throw ParseException("Empty token list", 0)
+    }
+}
\ No newline at end of file
diff --git a/docs/src/parser/files/InterfaceFileParser.kt b/docs/src/parser/files/InterfaceFileParser.kt
new file mode 100644
index 0000000..d68b12c
--- /dev/null
+++ b/docs/src/parser/files/InterfaceFileParser.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.files
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.elements.AnnotationParser
+import parser.elements.EntryParser
+import parser.elements.declarations.InterfaceDeclarationParser
+import parser.elements.declarations.MethodDeclarationParser
+
+class InterfaceFileParser(tokens: List<Token>) : AbstractFileParser(tokens) {
+
+    private val interfaceEntry: EntryParser by lazy {
+        //returns first matching element (i don't think there can be multiple interface declarations)
+        this.entries.find { it.declarationParser is InterfaceDeclarationParser }
+                ?: throw ClassNotFoundException("Unable to find an InterfaceDeclarationParser")
+    }
+    //need this for the cast
+    private val interfaceDecl: InterfaceDeclarationParser by lazy {
+        interfaceEntry.declarationParser as InterfaceDeclarationParser
+    }
+
+    /* top-level properties on the interface itself, ie. not its entries */
+
+    override val name: String get() = interfaceDecl.name
+    val extendsName: String? get() = interfaceDecl.extendsName
+    val extendsVersion: Float? get() = interfaceDecl.extendsVersion
+
+    //doc
+    val description get() = interfaceEntry.docParser.description
+    val docAnnotations get() = interfaceEntry.docParser.docAnnotationParsers //tag, arg?, description
+
+    val annotations: List<AnnotationParser> get() = interfaceEntry.annotationParsers //name, value
+    val methods: List<EntryParser> by lazy { getEntriesByDeclarationParser<MethodDeclarationParser>() }
+
+    companion object {
+        /**
+         * Searches tokens for an interface identifier.
+         * Maybe not the most accurate measurement, but good enough.
+         */
+        fun isInterface(tokens: List<Token>): Boolean {
+            val iter = tokens.listIterator()
+            var token: Token
+            var inDoc = false
+
+            while (iter.hasNext()) {
+                token = iter.next()
+                if (token.identifier == TokenGrammar.DOC_START) {
+                    inDoc = true
+                    continue
+                } else if (token.identifier == TokenGrammar.DOC_END) {
+                    inDoc = false
+                    continue
+
+                } else if (!inDoc && token.identifier == TokenGrammar.INTERFACE) {
+                    if (iter.next().category != TokenCategory.Word) break //no, try again
+
+                    return true
+                }
+            }
+            return false
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/docs/src/parser/files/TypesFileParser.kt
similarity index 79%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to docs/src/parser/files/TypesFileParser.kt
index d79e6ab..a903a6a 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/docs/src/parser/files/TypesFileParser.kt
@@ -13,7 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+package parser.files
+
+import lexer.Token
+
+class TypesFileParser(tokens: List<Token>) : AbstractFileParser(tokens) {
+    override val name = "types"
+}
\ No newline at end of file
diff --git a/docs/src/parser/files/package.kt b/docs/src/parser/files/package.kt
new file mode 100644
index 0000000..5b1cb81
--- /dev/null
+++ b/docs/src/parser/files/package.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser.files
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import java.text.ParseException
+
+data class PackageInfo(val name: String, val version: Float)
+
+/**
+ * Find and parse package info. Throw error if it can't find a valid declarationParser format.
+ * Example format: package android.hardware.audio@2.0;
+ */
+fun parsePackageInfo(tokens: List<Token>): PackageInfo {
+    val iter: ListIterator<Token> = tokens.listIterator()
+    var token: Token
+
+    while (iter.hasNext()) {
+        token = iter.next()
+
+        if (token.identifier == TokenGrammar.PACKAGE) {
+            //collect namespace
+            val pkgNameToks = mutableListOf<Token>()
+            while (iter.hasNext()) {
+                token = iter.next()
+                if (token.identifier != TokenGrammar.AT && token.identifier != TokenGrammar.SEMICOLON) {
+                    pkgNameToks.add(token)
+                } else {
+                    break
+                }
+            }
+            val pkgName = pkgNameToks.map { it.value }.joinToString("")
+
+            //step through format and test syntax
+            if (token.identifier != TokenGrammar.AT) break
+            token = iter.next()
+            if (token.category != TokenCategory.Number) break //version
+            val pkgVer = token.value.toFloat()
+            token = iter.next()
+            if (token.identifier != TokenGrammar.SEMICOLON) break
+
+            //hooray, a proper package format
+            return PackageInfo(pkgName, pkgVer)
+        }
+    }
+    throw ParseException("Unable to find a valid package declaration", 0)
+}
\ No newline at end of file
diff --git a/docs/src/parser/utils.kt b/docs/src/parser/utils.kt
new file mode 100644
index 0000000..77fa3d3
--- /dev/null
+++ b/docs/src/parser/utils.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package parser
+
+import lexer.Token
+
+/**
+ * Returns the next token in the iteration, without advancing the iteration.
+ */
+fun peekToken(iter: ListIterator<Token>): Token? {
+    if (iter.hasNext()) {
+        val token = iter.next()
+        iter.previous()
+        return token
+    } else {
+        return null
+    }
+}
+
+fun peekPreviousToken(iter: ListIterator<Token>): Token? {
+    if (iter.hasPrevious()) {
+        val token = iter.previous()
+        iter.next()
+        return token
+    } else {
+        return null
+    }
+}
\ No newline at end of file
diff --git a/docs/src/writer/elements/AbstractElement.kt b/docs/src/writer/elements/AbstractElement.kt
new file mode 100644
index 0000000..8be0bae
--- /dev/null
+++ b/docs/src/writer/elements/AbstractElement.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package writer.elements
+
+import parser.elements.EntryParser
+import parser.elements.declarations.AbstractDeclarationParser
+import writer.formatTextasHTML
+import writer.htmlEscape
+
+abstract class AbstractElement(parser: EntryParser) {
+    protected val docParser = parser.docParser
+    protected val annotationParsers = parser.annotationParsers
+
+    abstract protected val declarationParser: AbstractDeclarationParser //specify in subclass
+    abstract fun declaration(): String
+    abstract fun detailsRows(): String
+
+    open fun toHTML(): String {
+        return """
+<div>
+  <h3>${declarationParser.name}</h3>
+  <pre class="prettyprint devsite-disable-click-to-copy">${declaration()}</pre>
+  <div>
+    ${formatTextasHTML(docParser.description)}
+  </div>
+  <table class="details responsive">
+    <thead>
+      <th colspan="2">Details</th>
+    </thead>
+    <tbody>
+      ${detailsRows()}
+      ${annotationRows()}
+    </tbody>
+  </table>
+</div>
+""".trim()
+    }
+
+    private fun annotationRows(): String {
+        val sb = StringBuilder()
+        if (annotationParsers.isNotEmpty()) {
+            sb.append("""
+            <tr>
+              <td>Annotations</td>
+              <td>
+                <table class="function param responsive">
+                  <tbody>""")
+            //AnnotationParser => name:TokenGrammar, value:String
+            annotationParsers.forEach { arg ->
+                sb.append("""
+                <tr>
+                  <td>
+                    <code>${arg.name.value}</code>
+                  </td>
+                  <td>
+                    <div>${htmlEscape(arg.value)}</div>
+                  </td>
+                </tr>""")
+            }
+            sb.append("""
+                  </tbody>
+                </table>
+              </td>
+            </tr>""")
+        }
+        return sb.toString().trim()
+    }
+}
\ No newline at end of file
diff --git a/docs/src/writer/elements/CompoundElement.kt b/docs/src/writer/elements/CompoundElement.kt
new file mode 100644
index 0000000..d5fc180
--- /dev/null
+++ b/docs/src/writer/elements/CompoundElement.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package writer.elements
+
+import parser.elements.EntryParser
+import parser.elements.declarations.CompoundDeclarationParser
+import parser.elements.declarations.CompoundMemberDeclaration
+import writer.formatTextasHTML
+
+// used for structs and unions
+class CompoundElement(parser: EntryParser): AbstractElement(parser) {
+    //type, name, members [IMemberDeclaration]
+    override val declarationParser = parser.declarationParser as CompoundDeclarationParser
+
+    override fun declaration(): String {
+        val sb = StringBuilder()
+        sb.append("${declarationParser.type.value} ${declarationParser.name} {")
+        declarationParser.members.forEachIndexed { i, arg ->
+            val typedef = if (arg is CompoundMemberDeclaration) "${arg.typeDef.value} " else ""
+            sb.append("${typedef}${arg.type} ${arg.name}")
+            if (i < declarationParser.members.size-1) sb.append("; ")
+        }
+        sb.append("}")
+        return sb.toString()
+    }
+
+    override fun detailsRows(): String {
+        //build member rows
+        val sb = StringBuilder()
+        if (declarationParser.members.isNotEmpty()) {
+            sb.append("""
+            <tr>
+              <td>Members</td>
+              <td>
+                <table class="function param responsive">
+                  <tbody>""")
+            //type, name, tokens, typedef
+            declarationParser.members.forEach { arg ->
+                val fieldDesc = arg.docParser?.description?.let { formatTextasHTML(it, useParagraphs = false) } ?: ""
+                sb.append("""
+                <tr>
+                  <td>
+                    <code>${arg.name}</code>
+                  </td>
+                  <td>
+                    <div>${fieldDesc}</div>
+                  </td>
+                </tr>""")
+            }
+            sb.append("""
+                  </tbody>
+                </table>
+              </td>
+            </tr>""")
+        }
+        return sb.toString()
+    }
+}
\ No newline at end of file
diff --git a/docs/src/writer/elements/EnumElement.kt b/docs/src/writer/elements/EnumElement.kt
new file mode 100644
index 0000000..42d03fe
--- /dev/null
+++ b/docs/src/writer/elements/EnumElement.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package writer.elements
+
+import parser.elements.EntryParser
+import parser.elements.declarations.EnumDeclarationParser
+import writer.formatTextasHTML
+import writer.htmlEscape
+
+class EnumElement(parser: EntryParser) : AbstractElement(parser) {
+    //name [String], type [String], members [EnumMember: name, value?]
+    override val declarationParser = parser.declarationParser as EnumDeclarationParser
+
+    override fun declaration(): String {
+        return "enum ${declarationParser.name}: ${declarationParser.type}"
+    }
+
+    override fun detailsRows(): String {
+        //build member rows
+        val sb = StringBuilder()
+        if (declarationParser.members.isNotEmpty()) {
+            sb.append("""
+            <tr>
+              <td>Members</td>
+              <td>
+                <table class="function param responsive">
+                  <tbody>""")
+            //EnumMember => name, value?
+            declarationParser.members.forEach { arg ->
+                val fieldVal = arg.value?.let { " = ${htmlEscape(it)}" } ?: ""
+                val fieldDesc = arg.docParser?.description?.let { formatTextasHTML(it, useParagraphs = false) } ?: ""
+                sb.append("""
+                <tr>
+                  <td>
+                    <code>${htmlEscape(arg.name)}${fieldVal}</code>
+                  </td>
+                  <td>
+                    <div>$fieldDesc</div>
+                  </td>
+                </tr>""")
+            }
+            sb.append("""
+                  </tbody>
+                </table>
+              </td>
+            </tr>""")
+        }
+        return sb.toString().trim()
+    }
+}
\ No newline at end of file
diff --git a/docs/src/writer/elements/MethodElement.kt b/docs/src/writer/elements/MethodElement.kt
new file mode 100644
index 0000000..dcd6c2a
--- /dev/null
+++ b/docs/src/writer/elements/MethodElement.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package writer.elements
+
+import lexer.TokenGrammar
+import parser.config
+import parser.elements.EntryParser
+import parser.elements.declarations.ArgEntry
+import parser.elements.declarations.MethodDeclarationParser
+import writer.formatTextasHTML
+import writer.htmlEscape
+import writer.warn
+import java.text.ParseException
+
+class MethodElement(parser: EntryParser) : AbstractElement(parser) {
+    override val declarationParser = parser.declarationParser as MethodDeclarationParser
+
+    //prefix, params, returns
+    override fun declaration(): String {
+        val sb = StringBuilder()
+        declarationParser.prefix?.let { sb.append("${it} ") }
+        sb.append("${declarationParser.name} (")
+        declarationParser.params.forEachIndexed { i, arg ->
+            sb.append(htmlEscape("${arg.type} ${arg.name}"))
+            if (i < declarationParser.params.size-1) sb.append(", ")
+        }
+        sb.append(")")
+        if (declarationParser.returns.isNotEmpty()) {
+            sb.append("\ngenerates (")
+            declarationParser.returns.forEachIndexed { i, arg ->
+                sb.append(htmlEscape("${arg.type} ${arg.name}"))
+                if (i < declarationParser.returns.size-1) sb.append(", ")
+            }
+            sb.append(")")
+        }
+        return sb.toString()
+    }
+
+    override fun detailsRows(): String {
+        return """
+${buildPrefixRows()}
+${buildParamRows()}
+${buildReturnRows()}
+""".trim()
+    }
+
+    private fun getDocAnnotationDesc(tag: TokenGrammar, arg: ArgEntry): String {
+        return docParser.docAnnotationParsers
+                .filter { it.tag == tag }
+                .firstOrNull { it.arg == arg.name }
+                ?.let { formatTextasHTML(it.description, useParagraphs = false) }
+                ?: run {
+            val msg = "Missing @${tag.value} doc annotation for '${arg.type} ${arg.name}'"
+            if (config.errorOnly) {
+                throw ParseException(msg, 0)
+            } else {
+                warn(msg)
+                "" //return empty string if it can't find it
+            }
+        }
+    }
+
+    private fun buildPrefixRows(): String {
+        val sb = StringBuilder()
+        if (declarationParser.prefix != null) {
+            sb.append("""
+            <tr>
+              <td>RPC mode</td>
+              <td>
+                <table class="function param responsive">
+                  <tbody>
+                    <tr>
+                      <td>
+                        <code>${declarationParser.prefix}</code>
+                      </td>
+                    </tr>
+                  </tbody>
+                </table>
+              </td>
+            </tr>""")
+        }
+        return sb.toString()
+    }
+
+    private fun buildParamRows(): String {
+        //docParser.docAnnotationParsers //=> tag [TokenGrammer], arg?, description
+        val sb = StringBuilder()
+        if (declarationParser.params.isNotEmpty()) {
+            sb.append("""
+            <tr>
+              <td>Parameters</td>
+              <td>
+                <table class="function param responsive">
+                  <tbody>""")
+            declarationParser.params.forEach { arg ->
+                sb.append("""
+                <tr>
+                  <td>
+                    <code>${htmlEscape(arg.name)}</code>
+                  </td>
+                  <td>
+                    <div>${getDocAnnotationDesc(TokenGrammar.PARAM, arg)}</div>
+                  </td>
+                </tr>""")
+            }
+            sb.append("""
+                  </tbody>
+                </table>
+              </td>
+            </tr>""")
+        }
+        return sb.toString()
+    }
+
+    private fun buildReturnRows(): String {
+        val sb = StringBuilder()
+        if (declarationParser.returns.isNotEmpty()) {
+            sb.append("""
+            <tr>
+              <td>Generates</td>
+              <td>
+                <table class="function param responsive">
+                  <tbody>""")
+            declarationParser.returns.forEach { arg ->
+                sb.append("""
+                <tr>
+                  <td>
+                    <code>${htmlEscape(arg.name)}</code>
+                  </td>
+                  <td>
+                    <div>${getDocAnnotationDesc(TokenGrammar.RETURN, arg)}</div>
+                  </td>
+                </tr>""")
+            }
+            sb.append("""
+                  </tbody>
+                </table>
+              </td>
+            </tr>""")
+        }
+        return sb.toString()
+    }
+}
\ No newline at end of file
diff --git a/docs/src/writer/elements/TypedefElement.kt b/docs/src/writer/elements/TypedefElement.kt
new file mode 100644
index 0000000..9dea39a
--- /dev/null
+++ b/docs/src/writer/elements/TypedefElement.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package writer.elements
+
+import parser.elements.EntryParser
+import parser.elements.declarations.TypedefDeclarationParser
+import writer.formatTextasHTML
+
+class TypedefElement(parser: EntryParser) : AbstractElement(parser) {
+    //name, type
+    override val declarationParser = parser.declarationParser as TypedefDeclarationParser
+
+    override fun declaration(): String {
+        return "typedef ${declarationParser.type} ${declarationParser.name}"
+    }
+
+    override fun detailsRows() = "" //not used
+
+    override fun toHTML(): String {
+        return """
+<div>
+  <h3>${declarationParser.name}</h3>
+  <pre class="prettyprint devsite-disable-click-to-copy">${declaration()}</pre>
+  <div>
+    ${formatTextasHTML(docParser.description)}
+  </div>
+</div>
+""".trim()
+    }
+}
\ No newline at end of file
diff --git a/docs/src/writer/files/AbstractFileWriter.kt b/docs/src/writer/files/AbstractFileWriter.kt
new file mode 100644
index 0000000..0c75688
--- /dev/null
+++ b/docs/src/writer/files/AbstractFileWriter.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package writer.files
+
+import parser.config
+import java.nio.file.Path
+
+abstract class AbstractFileWriter() {
+    abstract val baseName: String
+    abstract val templateResource: String
+    abstract val path: Path
+
+    //init with template file contents on first use
+    var outStr: String = ""
+        get() = if (field.isEmpty()) resources.readResourceText(this.templateResource) else field
+
+    open fun replaceVars() {}
+
+    fun replaceVar(varName: String, newValue: String) {
+        outStr = outStr.replace(Regex("\\\$\\{?$varName}?"), newValue)
+    }
+
+    fun replaceVar(varName: String, newValue: () -> String) {
+        replaceVar(varName, newValue())
+    }
+
+    fun writeToFile(): Boolean {
+        replaceVars()
+        onWrite()
+
+        if (config.lintMode) {
+            return false
+        } else {
+            val dir = this.path.parent.toFile() //dir name
+            if (!dir.exists()) dir.mkdirs()
+            if (!dir.canWrite()) throw FileSystemException(dir, reason = "No write access to output directory")
+
+            val fp = this.path.toFile()
+            fp.bufferedWriter().use { it.write(this.outStr) }
+            if (!fp.isFile) throw FileSystemException(fp, reason = "Error writing file")
+            return true
+        }
+    }
+
+    open fun onWrite() {}
+
+    open fun printInfo() {
+        println("Name: ${this.baseName}")
+        println(" AbstractFileWriter:")
+        println("  class: ${javaClass.simpleName}")
+        println("  dest: ${this.path}")
+    }
+}
\ No newline at end of file
diff --git a/docs/src/writer/files/AbstractParserFileWriter.kt b/docs/src/writer/files/AbstractParserFileWriter.kt
new file mode 100644
index 0000000..96a54d2
--- /dev/null
+++ b/docs/src/writer/files/AbstractParserFileWriter.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package writer.files
+
+import parser.config
+import parser.files.AbstractFileParser
+import writer.elements.CompoundElement
+import writer.elements.EnumElement
+import writer.elements.TypedefElement
+import writer.getOutPath
+import java.nio.file.Path
+
+abstract class AbstractParserFileWriter(private val parser: AbstractFileParser) : AbstractFileWriter() {
+
+    override val baseName = parser.name
+    override val templateResource: String by lazy { "/resources/template/${this.baseName}.html" }
+    override val path: Path by lazy { getOutPath(parser, config.outDir) }
+
+    override fun replaceVars() {
+        replaceVar("name", parser.name)
+
+        replaceVar("propertyDefs") {
+            val sb = StringBuilder()
+
+            if (parser.typedefs.isNotEmpty()
+                    || parser.enums.isNotEmpty()
+                    || parser.structs.isNotEmpty()
+                    || parser.unions.isNotEmpty()) {
+                sb.append("<section>\n")
+                sb.append("<h2>Properties</h2>\n")
+                //typedefs
+                sb.append(parser.typedefs.map { TypedefElement(it).toHTML() }.joinToString("\n"))
+                //enums
+                sb.append(parser.enums.map { EnumElement(it).toHTML() }.joinToString("\n"))
+                //structs
+                sb.append(parser.structs.map { CompoundElement(it).toHTML() }.joinToString("\n"))
+                //unions
+                sb.append(parser.unions.map { CompoundElement(it).toHTML() }.joinToString("\n"))
+                sb.append("\n</section>\n")
+            }
+            sb.toString()
+        }
+    }
+
+    override fun printInfo() {
+        super.printInfo()
+        println(" AbstractParserFileWriter:")
+        println("  package: ${parser.packageName}")
+        println("  package ver: ${parser.packageVersion}")
+        println("  enums count: ${parser.enums.size}")
+        println("  structs count: ${parser.structs.size}")
+        println("  unions count: ${parser.unions.size}")
+        println("  typedefs count: ${parser.typedefs.size}")
+    }
+}
\ No newline at end of file
diff --git a/docs/src/writer/files/IndexFileWriter.kt b/docs/src/writer/files/IndexFileWriter.kt
new file mode 100644
index 0000000..3b686d3
--- /dev/null
+++ b/docs/src/writer/files/IndexFileWriter.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package writer.files
+
+import parser.LOG_NAME
+import parser.config
+import parser.files.AbstractFileParser
+import parser.files.InterfaceFileParser
+import writer.getDescSummaryText
+import writer.getOutPath
+import java.io.File
+import java.nio.file.Path
+import java.nio.file.Paths
+
+data class EntryData(val fullName: String, //package.BaseName
+                     val baseName: String,
+                     val packageName: String,
+                     val packageVersion: Float,
+                     val summary: String,
+                     val relPath: Path)
+
+class IndexFileWriter : AbstractFileWriter() {
+
+    override val baseName = "index"
+    override val templateResource = "/resources/template/${this.baseName}.html"
+    override val path: Path by lazy { Paths.get("${config.outDir}${File.separator}${this.baseName}.html") }
+
+    private val entries = mutableListOf<EntryData>()
+
+    fun addEntry(parser: AbstractFileParser) {
+        val summaryStr = when (parser) {
+            is InterfaceFileParser -> getDescSummaryText(parser.description)
+            else -> ""
+        }
+        entries.add(EntryData(
+                fullName = "${parser.packageName}.${parser.name}",
+                baseName = parser.name,
+                packageName = parser.packageName,
+                packageVersion = parser.packageVersion,
+                summary = summaryStr,
+                relPath = config.outDir.relativize(getOutPath(parser, config.outDir))
+        ))
+    }
+
+    override fun printInfo() {
+        super.printInfo()
+        println( "IndexFileWriter:")
+        println("  entries: ${this.entries.size}")
+    }
+
+    /*
+     * HTML index file
+     */
+
+    override fun replaceVars() {
+        replaceVar("title", "Index")
+
+        replaceVar("entries") {
+            val sb = StringBuilder()
+            if (entries.isNotEmpty()) {
+                entries.sortWith(EntryNameComparator())
+                sb.append(buildEntryTable())
+            }
+            sb.toString()
+        }
+    }
+
+    private fun buildEntryTable(): String {
+        return """
+<table>
+  <tr>
+    <th>Entry</th>
+    <th>Version</th>
+    <th>Summary</th>
+  </tr>
+${entries.map { buildClassEntry(it) }.joinToString("\n")}
+</table>
+""".trim()
+    }
+
+    private fun buildClassEntry(entry: EntryData): String {
+        return """
+<tr>
+  <td>
+<a href="${entry.relPath}">${entry.fullName}</a>
+  </td>
+  <td>
+${entry.packageVersion}
+  </td>
+  <td>
+${entry.summary}
+  </td>
+</tr>
+""".trim()
+    }
+
+    private class EntryNameComparator : Comparator<EntryData> {
+        override fun compare(entry1: EntryData, entry2: EntryData): Int {
+            return if (entry1.fullName != entry2.fullName) {
+                //sort on name first, alphabetic
+                when {
+                    entry1.fullName < entry2.fullName -> -1
+                    entry1.fullName > entry2.fullName -> 1
+                    else -> 0
+                }
+            } else {
+                //if same name, reverse sort on pkg version (highest first)
+                when {
+                    entry1.packageVersion < entry2.packageVersion -> 1
+                    entry1.packageVersion > entry2.packageVersion -> -1
+                    else -> 0
+                }
+            }
+        }
+    }
+
+    /*
+     * YAML toc file
+     */
+
+    private val tocFileName = "_book.yaml"
+    private val tocFileRelPath = "/reference/hidl"
+    private val tocOutPath: Path by lazy { Paths.get("${config.outDir}${File.separator}${this.tocFileName}") }
+
+    //write toc yaml file after html index
+    override fun onWrite() {
+        super.onWrite()
+        if (!config.lintMode) {
+            val fp = tocOutPath.toFile()
+            fp.bufferedWriter().use {
+                it.write(buildTocHeader())
+                it.write(buildTocEntries(collectPackages()))
+            }
+            if (!fp.isFile) throw FileSystemException(fp, reason = "Error writing file")
+            println("$LOG_NAME Wrote toc: $tocOutPath")
+        }
+    }
+
+    private fun buildTocHeader(): String {
+        return """
+# Generated by hidl-doc
+toc:
+- title: Index
+  path: $tocFileRelPath
+""".trimStart()
+    }
+
+    private fun buildTocEntries(pkgEntries: Map<String, List<EntryData>>): String {
+        val sb = StringBuilder()
+
+        for ((pkgName, entryList) in pkgEntries) {
+            sb.append("- title: $pkgName\n  section:\n")
+
+            entryList.forEach { entry ->
+                sb.append("  - title: ${entry.baseName} @${entry.packageVersion}\n")
+                sb.append("    path: ${tocFileRelPath}/${entry.relPath}\n")
+            }
+        }
+        return sb.toString()
+    }
+
+    private fun collectPackages(): Map<String, List<EntryData>> {
+        val pkgEntries = mutableMapOf<String, MutableList<EntryData>>()
+
+        this.entries.forEach { entry ->
+            if (pkgEntries.containsKey(entry.packageName)) {
+                pkgEntries[entry.packageName]!!.add(entry)
+            } else {
+                pkgEntries[entry.packageName] = mutableListOf(entry)
+            }
+        }
+        //sort on package name. entries *should* already be sorted
+        return pkgEntries.toSortedMap()
+    }
+}
\ No newline at end of file
diff --git a/docs/src/writer/files/InterfaceFileWriter.kt b/docs/src/writer/files/InterfaceFileWriter.kt
new file mode 100644
index 0000000..a0fefa6
--- /dev/null
+++ b/docs/src/writer/files/InterfaceFileWriter.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package writer.files
+
+import parser.files.InterfaceFileParser
+import writer.elements.MethodElement
+import writer.formatTextasHTML
+
+class InterfaceFileWriter(private val parser: InterfaceFileParser) : AbstractParserFileWriter(parser) {
+
+    override var templateResource = "/resources/template/interface.html"
+
+    override fun replaceVars() {
+        super.replaceVars()
+        replaceVar("header", buildHeader())
+
+        replaceVar("methodDefs") {
+            val sb = StringBuilder()
+            if (parser.methods.isNotEmpty()) {
+                sb.append("<section>\n")
+                sb.append("<h2>Methods</h2>\n")
+                sb.append(parser.methods.map { MethodElement(it).toHTML() }.joinToString("\n"))
+                sb.append("\n</section>\n")
+            }
+            sb.toString()
+        }
+    }
+
+    private fun buildHeader(): String {
+        return """
+<header>
+  <div id="api-info-block">
+    Package: <code>${parser.packageName}@${parser.packageVersion}</code>
+  </div>
+  <h1>${parser.name}</h1>
+  <code>${buildDeclaration()}</code>
+  ${formatTextasHTML(parser.description)}
+</header>
+""".trim()
+    }
+
+    private fun buildDeclaration(): String {
+        val sb = StringBuilder()
+        sb.append("interface ${parser.name}")
+        if (parser.extendsName != null) {
+            sb.append(" extends ")
+            if (parser.extendsVersion != null) sb.append("@${parser.extendsVersion}::")
+            sb.append(parser.extendsName)
+        }
+        return sb.toString()
+    }
+
+    override fun printInfo() {
+        super.printInfo()
+        println(" InterfaceFileWriter:")
+        println("  Interface: ${parser.name} extends ${parser.extendsName}::${parser.extendsVersion}")
+        val methodEntries = parser.methods.map { MethodElement(it) }
+        println("  method count: ${methodEntries.size}")
+    }
+}
\ No newline at end of file
diff --git a/docs/src/writer/files/TypesFileWriter.kt b/docs/src/writer/files/TypesFileWriter.kt
new file mode 100644
index 0000000..85b9a60
--- /dev/null
+++ b/docs/src/writer/files/TypesFileWriter.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package writer.files
+
+import parser.files.TypesFileParser
+
+class TypesFileWriter(private val parser: TypesFileParser) : AbstractParserFileWriter(parser) {
+
+    override var templateResource = "/resources/template/types.html"
+
+    override fun replaceVars() {
+        super.replaceVars()
+        replaceVar("header", buildHeader())
+    }
+
+    private fun buildHeader(): String {
+        return """
+<header>
+  <div id="api-info-block">
+    Package: <code>${parser.packageName}@${parser.packageVersion}</code>
+  </div>
+  <h1>${parser.name}</h1>
+</header>
+""".trim()
+    }
+}
\ No newline at end of file
diff --git a/docs/src/writer/files/resources.kt b/docs/src/writer/files/resources.kt
new file mode 100644
index 0000000..25ba4b8
--- /dev/null
+++ b/docs/src/writer/files/resources.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package writer.files
+
+import parser.config
+import java.io.FileNotFoundException
+import java.nio.file.Path
+
+object resources {
+
+    private val resourceCache = mutableMapOf<String, String>() //name(path) => contents
+
+    fun readResourceText(name: String): String {
+        return resourceCache.getOrElse(name) {
+            val input = javaClass.getResourceAsStream(name)
+                    ?: throw FileNotFoundException("Unable to locate file resource: $name")
+
+            val contents = input.bufferedReader().use { it.readText() }
+            resourceCache[name] = contents
+            contents
+        }
+    }
+
+    fun copyToFile(resourceName: String, outPath: Path): Boolean {
+        val contents = readResourceText(resourceName)
+
+        if (config.lintMode) {
+            return false
+        } else {
+            val dir = outPath.parent.toFile() //dir name
+            if (!dir.exists()) dir.mkdirs()
+            if (!dir.canWrite()) throw FileSystemException(dir, reason = "No write access to output directory")
+
+            val fp = outPath.toFile()
+            fp.bufferedWriter().use { it.write(contents) }
+            if (!fp.isFile) throw FileSystemException(fp, reason = "Error writing file")
+            return true
+        }
+    }
+}
\ No newline at end of file
diff --git a/docs/src/writer/formatutils.kt b/docs/src/writer/formatutils.kt
new file mode 100644
index 0000000..8afeb6d
--- /dev/null
+++ b/docs/src/writer/formatutils.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package writer
+
+import lexer.Token
+import parser.files.AbstractFileParser
+import java.io.File
+import java.nio.file.Path
+import java.nio.file.Paths
+
+fun warn(msg: String) {
+    System.err.println("WARNING: $msg")
+}
+
+/**
+ * Get values of tokens, useful for debugging.
+ */
+fun tokenValues(tokens: List<Token>): String {
+    return tokens.map { it.value }.joinToString("|")
+}
+
+/**
+ * Escape string for HTML.
+ */
+fun htmlEscape(string: String): String {
+    val out = StringBuilder(Math.max(16, string.length))
+    string.toCharArray().forEach { c ->
+        if (c.toInt() > 127 || c == '"' || c == '<' || c == '>' || c == '&' || c == '$' || c == '{' || c == '}') {
+            out.append("&#")
+            out.append(c.toInt())
+            out.append(';')
+        } else {
+            out.append(c)
+        }
+    }
+    return out.toString()
+}
+
+/**
+ * Used to display description text.
+ */
+fun formatTextasHTML(string: String, useParagraphs: Boolean = true): String {
+    if (string.isEmpty()) return string
+
+    val sb = StringBuilder()
+    if (useParagraphs) sb.append("<p>")
+    //match and replace empty lines
+    val replaceText = if (useParagraphs) "</p>\n<p>" else "<br>\n"
+    sb.append(htmlEscape(string.trim()).replace(Regex("\\s*\n\n\\s*"), replaceText))
+    if (useParagraphs) sb.append("</p>")
+    return sb.toString()
+}
+
+private val summaryRegex = Regex("\\.|\n\n") //match period or empty line
+
+/**
+ * Given a block of description text, return the first sentence.
+ */
+fun getDescSummaryText(string: String): String {
+    return if (string.isEmpty()) {
+        string
+    } else {
+        val s = string.trimStart() // remove any beginning empty lines/whitespace
+        val sb = StringBuilder(summaryRegex.split(s)[0])
+        if (sb[sb.length - 1] != '.') sb.append(".") // add period, if needed
+        formatTextasHTML(sb.toString())
+    }
+}
+
+/**
+ * Return the out file path for a given parser.
+ */
+fun getOutPath(parser: AbstractFileParser, outDir: Path): Path {
+    val pkgPath = parser.packageName.replace(".", File.separator)
+    val dirPath = "${outDir}${File.separator}${pkgPath}${File.separator}${parser.packageVersion}"
+    return Paths.get("${dirPath}${File.separator}${parser.name}.html")
+}
\ No newline at end of file
diff --git a/generateCpp.cpp b/generateCpp.cpp
index 7292732..fa2a3ca 100644
--- a/generateCpp.cpp
+++ b/generateCpp.cpp
@@ -18,9 +18,10 @@
 
 #include "Coordinator.h"
 #include "EnumType.h"
-#include "Interface.h"
 #include "HidlTypeAssertion.h"
+#include "Interface.h"
 #include "Method.h"
+#include "Reference.h"
 #include "ScalarType.h"
 #include "Scope.h"
 
@@ -33,38 +34,6 @@
 
 namespace android {
 
-status_t AST::generateCpp(const std::string &outputPath) const {
-    status_t err = generateCppHeaders(outputPath);
-
-    if (err == OK) {
-        err = generateCppSources(outputPath);
-    }
-
-    return err;
-}
-
-status_t AST::generateCppHeaders(const std::string &outputPath) const {
-    status_t err = generateInterfaceHeader(outputPath);
-
-    if (err == OK) {
-        err = generateStubHeader(outputPath);
-    }
-
-    if (err == OK) {
-        err = generateHwBinderHeader(outputPath);
-    }
-
-    if (err == OK) {
-        err = generateProxyHeader(outputPath);
-    }
-
-    if (err == OK) {
-        err = generatePassthroughHeader(outputPath);
-    }
-
-    return err;
-}
-
 void AST::getPackageComponents(
         std::vector<std::string> *components) const {
     mPackage.getPackageComponents(components);
@@ -175,173 +144,11 @@
         << "::android::sp<" << interfaceName << "> " << interfaceName << "::" << functionName << "("
         << "const std::string &serviceName, const bool getStub) ";
     out.block([&] {
-        out << "using ::android::hardware::defaultServiceManager;\n";
-        out << "using ::android::hardware::details::waitForHwService;\n";
-        out << "using ::android::hardware::getPassthroughServiceManager;\n";
-        out << "using ::android::hardware::Return;\n";
-        out << "using ::android::sp;\n";
-        out << "using Transport = ::android::hidl::manager::V1_0::IServiceManager::Transport;\n\n";
-
-        out << "sp<" << interfaceName << "> iface = nullptr;\n";
-
-        out.endl();
-
-        out << "const sp<::android::hidl::manager::V1_0::IServiceManager> sm"
-            << " = defaultServiceManager();\n";
-
-        out.sIf("sm == nullptr", [&] {
-            // hwbinder is not available on this device, so future tries
-            // would also be null. I can only return nullptr.
-            out << "ALOGE(\"getService: defaultServiceManager() is null\");\n"
-                << "return nullptr;\n";
-        }).endl().endl();
-
-        out << "Return<Transport> transportRet = sm->getTransport("
-            << interfaceName << "::descriptor, serviceName);\n\n";
-
-        out.sIf("!transportRet.isOk()", [&] {
-            out << "ALOGE(\"getService: defaultServiceManager()->getTransport returns %s\", "
-                << "transportRet.description().c_str());\n";
-            out << "return nullptr;\n";
-        }).endl();
-
-        out << "Transport transport = transportRet;\n";
-        out << "const bool vintfHwbinder = (transport == Transport::HWBINDER);\n"
-            << "const bool vintfPassthru = (transport == Transport::PASSTHROUGH);\n\n";
-
-        // This means that you must set TREBLE_TESTING_OVERRIDE when running a test such
-        // as hidl_test. Ideally these binaries set this value themselves. This allows
-        // test modules to dynamically add and unset services even though they are not
-        // declared in the device manifest. This prevents a problem where framework
-        // changes are accidentally made in a way that is not backwards compatible. For
-        // instance, consider the following situation for two devices developed in the
-        // same tree:
-        // A: serves @1.1::IFoo, declares @1.0::IFoo (incorrect)
-        // B: serves @1.0::IFoo, declares @1.0::IFoo (correct configuration)
-        // If development is done on device A, then framework code like: "V1_1::IFoo::
-        // getService()->doV1_0Api()" will work. However, this will unintentionally break
-        // the feature for devices like device B for which "V1_1::IFoo::getService()
-        // will return nullptr. In order to prevent problems like this, we only allow
-        // fetching an interface if it is declared in a VINTF manifest.
-        out << "#ifdef __ANDROID_TREBLE__\n\n"
-            << "#ifdef __ANDROID_DEBUGGABLE__\n"
-            << "const char* env = std::getenv(\"TREBLE_TESTING_OVERRIDE\");\n"
-            << "const bool trebleTestingOverride =  env && !strcmp(env, \"true\");\n"
-            << "const bool vintfLegacy = (transport == Transport::EMPTY) && trebleTestingOverride;\n"
-            << "#else // __ANDROID_TREBLE__ but not __ANDROID_DEBUGGABLE__\n"
-            << "const bool trebleTestingOverride = false;\n"
-            << "const bool vintfLegacy = false;\n"
-            << "#endif // __ANDROID_DEBUGGABLE__\n\n"
-            << "#else // not __ANDROID_TREBLE__\n"
-            << "const char* env = std::getenv(\"TREBLE_TESTING_OVERRIDE\");\n"
-            << "const bool trebleTestingOverride =  env && !strcmp(env, \"true\");\n"
-            << "const bool vintfLegacy = (transport == Transport::EMPTY);\n\n"
-            << "#endif // __ANDROID_TREBLE__\n\n";
-
-        // if (getStub) {
-        //     getPassthroughServiceManager()->get only once.
-        // } else {
-        //     if (vintfHwbinder) {
-        //         while (no alive service) {
-        //             if (have already tried to get service)
-        //                 waitForHwService
-        //             defaultServiceManager()->get
-        //         }
-        //     } else if (vintfLegacy) {
-        //         defaultServiceManager()->get only once.
-        //         getPassthroughServiceManager()->get only once.
-        //     } else if (vintfPassthru) {
-        //         getPassthroughServiceManager()->get only once.
-        //     }
-        // }
-
-        out.sFor("int tries = 0; !getStub && (vintfHwbinder || (vintfLegacy && tries == 0)); tries++", [&] {
-            if (!isTry) {
-                out.sIf("tries > 1", [&] {
-                    // sleep only after the first time we've called waitForHwService.
-                    out << "ALOGI(\"" << functionName << ": Will do try %d for %s/%s in 1s...\", tries, "
-                        << interfaceName << "::descriptor, serviceName.c_str());\n"
-                        << "sleep(1);\n";
-                }).endl();
-
-                out.sIf("vintfHwbinder && tries > 0", [&] {
-                    out << "waitForHwService("
-                        << interfaceName << "::descriptor, serviceName);\n";
-                }).endl();
-            }
-
-            out << "Return<sp<" << gIBaseFqName.cppName() << ">> ret = \n";
-            out.indent(2, [&] {
-                out << "sm->get(" << interfaceName << "::descriptor, serviceName);\n";
-            });
-
-            out.sIf("!ret.isOk()", [&] {
-                // hwservicemanager fails, may be security issue
-                out << "ALOGE(\"" << interfaceName << ": defaultServiceManager()->get returns %s\", "
-                    << "ret.description().c_str());\n"
-                    << "break;\n";
-            }).endl();
-
-            out << "sp<" << gIBaseFqName.cppName() << "> base = ret;\n";
-            out.sIf("base == nullptr", [&] {
-                // if tries > 0: race condition. hwservicemanager drops the service
-                // from waitForHwService to here
-                out.sIf("tries > 0", [&] {
-                    out << "ALOGW(\"" << interfaceName << ": found null hwbinder interface\");\n";
-                });
-                out << (isTry ? "break" : "continue")
-                    << ";\n";
-            }).endl();
-            out << "Return<sp<" << interfaceName
-                << ">> castRet = " << interfaceName << "::castFrom(base, true /* emitError */);\n";
-            out.sIf("!castRet.isOk()", [&] {
-                out.sIf("castRet.isDeadObject()", [&] {
-                    // service is dead (castFrom cannot call interfaceChain)
-                    out << "ALOGW(\"" << interfaceName << ": found dead hwbinder service\");\n"
-                        << (isTry ? "break" : "continue")
-                        << ";\n";
-                }).sElse([&] {
-                    out << "ALOGW(\"" << interfaceName << ": cannot call into hwbinder service: %s"
-                        << "; No permission? Check for selinux denials.\", "
-                        << "castRet.description().c_str());\n"
-                        << "break;\n";
-                }).endl();
-            }).endl();
-            out << "iface = castRet;\n";
-            out.sIf("iface == nullptr", [&] {
-                // returned service isn't of correct type; this is a bug
-                // to hwservicemanager or to the service itself (interfaceChain
-                // is not consistent).
-                out << "ALOGW(\"" << interfaceName << ": received incompatible service; bug in hwservicemanager?\");\n"
-                    << "break;\n";
-            }).endl();
-
-            out << "return iface;\n";
-        }).endl();
-
-        out.sIf("getStub || vintfPassthru || vintfLegacy", [&] {
-            out << "const sp<::android::hidl::manager::V1_0::IServiceManager> pm"
-                << " = getPassthroughServiceManager();\n";
-
-            out.sIf("pm != nullptr", [&] () {
-                out << "Return<sp<" << gIBaseFqName.cppName() << ">> ret = \n";
-                out.indent(2, [&] {
-                    out << "pm->get(" << interfaceName << "::descriptor" << ", serviceName);\n";
-                });
-                out.sIf("ret.isOk()", [&] {
-                    out << "sp<" << gIBaseFqName.cppName()
-                        << "> baseInterface = ret;\n";
-                    out.sIf("baseInterface != nullptr", [&]() {
-                        out << "iface = " << interfaceName << "::castFrom(baseInterface);\n";
-                        out.sIf("!getStub || trebleTestingOverride", [&] () {
-                            out << "iface = new " << fqName.getInterfacePassthroughName() << "(iface);\n";
-                        }).endl();
-                    }).endl();
-                }).endl();
-            }).endl();
-        }).endl();
-
-        out << "return iface;\n";
+        out << "return ::android::hardware::details::getServiceInternal<"
+            << fqName.getInterfaceProxyName()
+            << ">(serviceName, "
+            << (!isTry ? "true" : "false") // retry
+            << ", getStub);\n";
     }).endl().endl();
 }
 
@@ -397,25 +204,9 @@
     }).endl().endl();
 }
 
-status_t AST::generateInterfaceHeader(const std::string &outputPath) const {
+void AST::generateInterfaceHeader(Formatter& out) const {
     const Interface *iface = getInterface();
     std::string ifaceName = iface ? iface->localName() : "types";
-
-    std::string path = outputPath;
-    path.append(mCoordinator->convertPackageRootToPath(mPackage));
-    path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
-    path.append(ifaceName);
-    path.append(".h");
-
-    CHECK(Coordinator::MakeParentHierarchy(path));
-    FILE *file = fopen(path.c_str(), "w");
-
-    if (file == NULL) {
-        return -errno;
-    }
-
-    Formatter out(file);
-
     const std::string guard = makeHeaderGuard(ifaceName);
 
     out << "#ifndef " << guard << "\n";
@@ -467,13 +258,10 @@
 
         out.indent();
 
+        generateCppTag(out, "android::hardware::details::i_tag");
     }
 
-    status_t err = emitTypeDeclarations(out);
-
-    if (err != OK) {
-        return err;
-    }
+    emitTypeDeclarations(out);
 
     if (iface) {
         out << "virtual bool isRemote() const ";
@@ -488,7 +276,7 @@
             out << "\n";
 
             const bool returnsValue = !method->results().empty();
-            const TypedVar *elidedReturn = method->canElideCallback();
+            const NamedReference<Type>* elidedReturn = method->canElideCallback();
 
             if (elidedReturn == nullptr && returnsValue) {
                 out << "using "
@@ -500,6 +288,8 @@
 
             method->dumpAnnotations(out);
 
+            method->emitDocComment(out);
+
             if (elidedReturn) {
                 out << "virtual ::android::hardware::Return<";
                 out << elidedReturn->type().getCppResultType() << "> ";
@@ -548,37 +338,20 @@
         out << "};\n\n";
     }
 
-    err = mRootScope.emitGlobalTypeDeclarations(out);
-
-    if (err != OK) {
-        return err;
-    }
+    mRootScope.emitPackageTypeDeclarations(out);
 
     out << "\n";
     enterLeaveNamespace(out, false /* enter */);
 
-    out << "\n#endif  // " << guard << "\n";
+    mRootScope.emitGlobalTypeDeclarations(out);
 
-    return OK;
+    out << "\n#endif  // " << guard << "\n";
 }
 
-status_t AST::generateHwBinderHeader(const std::string &outputPath) const {
+void AST::generateHwBinderHeader(Formatter& out) const {
     const Interface *iface = getInterface();
     std::string klassName = iface ? iface->getHwName() : "hwtypes";
 
-    std::string path = outputPath;
-    path.append(mCoordinator->convertPackageRootToPath(mPackage));
-    path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
-    path.append(klassName + ".h");
-
-    FILE *file = fopen(path.c_str(), "w");
-
-    if (file == NULL) {
-        return -errno;
-    }
-
-    Formatter out(file);
-
     const std::string guard = makeHeaderGuard(klassName);
 
     out << "#ifndef " << guard << "\n";
@@ -607,25 +380,19 @@
 
     enterLeaveNamespace(out, true /* enter */);
 
-    status_t err = mRootScope.emitGlobalHwDeclarations(out);
-    if (err != OK) {
-        return err;
-    }
+    mRootScope.emitPackageHwDeclarations(out);
 
     enterLeaveNamespace(out, false /* enter */);
 
     out << "\n#endif  // " << guard << "\n";
-
-    return OK;
 }
 
-status_t AST::emitTypeDeclarations(Formatter &out) const {
+void AST::emitTypeDeclarations(Formatter& out) const {
     return mRootScope.emitTypeDeclarations(out);
 }
 
-static void wrapPassthroughArg(Formatter &out,
-        const TypedVar *arg, bool addPrefixToName,
-        std::function<void(void)> handleError) {
+static void wrapPassthroughArg(Formatter& out, const NamedReference<Type>* arg,
+                               bool addPrefixToName, std::function<void(void)> handleError) {
     if (!arg->type().isInterface()) {
         return;
     }
@@ -638,11 +405,9 @@
     out.sIf(name + " != nullptr && !" + name + "->isRemote()", [&] {
         out << wrappedName
             << " = "
-            << iface.fqName().cppName()
-            << "::castFrom(::android::hardware::details::wrapPassthrough<"
-            << iface.fqName().cppName()
-            << ">("
-            << name << "));\n";
+            << "::android::hardware::details::wrapPassthrough("
+            << name
+            << ");\n";
         out.sIf(wrappedName + " == nullptr", [&] {
             // Fatal error. Happens when the BsFoo class is not found in the binary
             // or any dynamic libraries.
@@ -653,8 +418,7 @@
     }).endl().endl();
 }
 
-status_t AST::generatePassthroughMethod(Formatter &out,
-                                        const Method *method) const {
+void AST::generatePassthroughMethod(Formatter& out, const Method* method) const {
     method->generateCppSignature(out);
 
     out << " {\n";
@@ -665,11 +429,11 @@
         method->cppImpl(IMPL_PASSTHROUGH, out);
         out.unindent();
         out << "}\n\n";
-        return OK;
+        return;
     }
 
     const bool returnsValue = !method->results().empty();
-    const TypedVar *elidedReturn = method->canElideCallback();
+    const NamedReference<Type>* elidedReturn = method->canElideCallback();
 
     if (returnsValue && elidedReturn == nullptr) {
         generateCheckNonNull(out, "_hidl_cb");
@@ -779,11 +543,9 @@
 
     out.unindent();
     out << "}\n";
-
-    return OK;
 }
 
-status_t AST::generateMethods(Formatter &out, MethodGenerator gen, bool includeParent) const {
+void AST::generateMethods(Formatter& out, const MethodGenerator& gen, bool includeParent) const {
     const Interface* iface = mRootScope.getInterface();
 
     const Interface *prevIterface = nullptr;
@@ -804,52 +566,32 @@
                 << " follow.\n";
             prevIterface = superInterface;
         }
-        status_t err = gen(method, superInterface);
-
-        if (err != OK) {
-            return err;
-        }
+        gen(method, superInterface);
     }
 
     out << "\n";
-
-    return OK;
 }
 
 void AST::generateTemplatizationLink(Formatter& out) const {
     out << "typedef " << mRootScope.getInterface()->localName() << " Pure;\n\n";
 }
 
-status_t AST::generateStubHeader(const std::string &outputPath) const {
-    if (!AST::isInterface()) {
-        // types.hal does not get a stub header.
-        return OK;
-    }
+void AST::generateCppTag(Formatter& out, const std::string& tag) const {
+    out << "typedef " << tag << " _hidl_tag;\n\n";
+}
+
+void AST::generateStubHeader(Formatter& out) const {
+    CHECK(AST::isInterface());
 
     const Interface* iface = mRootScope.getInterface();
     const std::string klassName = iface->getStubName();
-
-    std::string path = outputPath;
-    path.append(mCoordinator->convertPackageRootToPath(mPackage));
-    path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
-    path.append(klassName);
-    path.append(".h");
-
-    CHECK(Coordinator::MakeParentHierarchy(path));
-    FILE *file = fopen(path.c_str(), "w");
-
-    if (file == NULL) {
-        return -errno;
-    }
-
-    Formatter out(file);
-
     const std::string guard = makeHeaderGuard(klassName);
 
     out << "#ifndef " << guard << "\n";
     out << "#define " << guard << "\n\n";
 
     generateCppPackageInclude(out, mPackage, iface->getHwName());
+
     out << "\n";
 
     enterLeaveNamespace(out, true /* enter */);
@@ -891,41 +633,40 @@
 
     out.endl();
     generateTemplatizationLink(out);
+    generateCppTag(out, "android::hardware::details::bnhw_tag");
 
-    out << "::android::sp<" << iface->localName() << "> getImpl() { return _hidl_mImpl; };\n";
+    out << "::android::sp<" << iface->localName() << "> getImpl() { return _hidl_mImpl; }\n";
 
-    status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
-        if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PROXY)) {
-            return OK;
-        }
+    generateMethods(out,
+                    [&](const Method* method, const Interface*) {
+                        if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PROXY)) {
+                            return;
+                        }
 
-        out << "static ::android::status_t _hidl_" << method->name() << "(\n";
+                        out << "static ::android::status_t _hidl_" << method->name() << "(\n";
 
-        out.indent(2, [&] {
-            out << "::android::hidl::base::V1_0::BnHwBase* _hidl_this,\n"
-                << "const ::android::hardware::Parcel &_hidl_data,\n"
-                << "::android::hardware::Parcel *_hidl_reply,\n"
-                << "TransactCallback _hidl_cb);\n";
-        }).endl().endl();
-
-        return OK;
-    }, false /* include parents */);
-
-    if (err != OK) {
-        return err;
-    }
+                        out.indent(2,
+                                   [&] {
+                                       out << "::android::hidl::base::V1_0::BnHwBase* _hidl_this,\n"
+                                           << "const ::android::hardware::Parcel &_hidl_data,\n"
+                                           << "::android::hardware::Parcel *_hidl_reply,\n"
+                                           << "TransactCallback _hidl_cb);\n";
+                                   })
+                            .endl()
+                            .endl();
+                    },
+                    false /* include parents */);
 
     out.unindent();
     out << "private:\n";
     out.indent();
 
-
-    err = generateMethods(out, [&](const Method *method, const Interface *iface) {
+    generateMethods(out, [&](const Method* method, const Interface* iface) {
         if (!method->isHidlReserved() || !method->overridesCppImpl(IMPL_STUB_IMPL)) {
-            return OK;
+            return;
         }
         const bool returnsValue = !method->results().empty();
-        const TypedVar *elidedReturn = method->canElideCallback();
+        const NamedReference<Type>* elidedReturn = method->canElideCallback();
 
         if (elidedReturn == nullptr && returnsValue) {
             out << "using " << method->name() << "_cb = "
@@ -934,11 +675,7 @@
         }
         method->generateCppSignature(out);
         out << ";\n";
-        return OK;
     });
-    if (err != OK) {
-        return err;
-    }
 
     out << "::android::sp<" << iface->localName() << "> _hidl_mImpl;\n";
     out.unindent();
@@ -947,34 +684,16 @@
     enterLeaveNamespace(out, false /* enter */);
 
     out << "\n#endif  // " << guard << "\n";
-
-    return OK;
 }
 
-status_t AST::generateProxyHeader(const std::string &outputPath) const {
+void AST::generateProxyHeader(Formatter& out) const {
     if (!AST::isInterface()) {
         // types.hal does not get a proxy header.
-        return OK;
+        return;
     }
 
     const Interface* iface = mRootScope.getInterface();
     const std::string proxyName = iface->getProxyName();
-
-    std::string path = outputPath;
-    path.append(mCoordinator->convertPackageRootToPath(mPackage));
-    path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
-    path.append(proxyName);
-    path.append(".h");
-
-    CHECK(Coordinator::MakeParentHierarchy(path));
-    FILE *file = fopen(path.c_str(), "w");
-
-    if (file == NULL) {
-        return -errno;
-    }
-
-    Formatter out(file);
-
     const std::string guard = makeHeaderGuard(proxyName);
 
     out << "#ifndef " << guard << "\n";
@@ -1006,44 +725,36 @@
         << "\n\n";
 
     generateTemplatizationLink(out);
+    generateCppTag(out, "android::hardware::details::bphw_tag");
 
     out << "virtual bool isRemote() const override { return true; }\n\n";
 
-    status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
-        if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PROXY)) {
-            return OK;
-        }
+    generateMethods(
+        out,
+        [&](const Method* method, const Interface*) {
+            if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PROXY)) {
+                return;
+            }
 
-        out << "static ";
-        method->generateCppReturnType(out);
-        out << " _hidl_"
-            << method->name()
-            << "("
-            << "::android::hardware::IInterface* _hidl_this, "
-            << "::android::hardware::details::HidlInstrumentor *_hidl_this_instrumentor";
+            out << "static ";
+            method->generateCppReturnType(out);
+            out << " _hidl_" << method->name() << "("
+                << "::android::hardware::IInterface* _hidl_this, "
+                << "::android::hardware::details::HidlInstrumentor *_hidl_this_instrumentor";
 
-        if (!method->hasEmptyCppArgSignature()) {
-            out << ", ";
-        }
-        method->emitCppArgSignature(out);
-        out << ");\n";
-        return OK;
-    }, false /* include parents */);
+            if (!method->hasEmptyCppArgSignature()) {
+                out << ", ";
+            }
+            method->emitCppArgSignature(out);
+            out << ");\n";
+        },
+        false /* include parents */);
 
-    if (err != OK) {
-        return err;
-    }
-
-    err = generateMethods(out, [&](const Method *method, const Interface *) {
+    generateMethods(out, [&](const Method* method, const Interface*) {
         method->generateCppSignature(out);
         out << " override;\n";
-        return OK;
     });
 
-    if (err != OK) {
-        return err;
-    }
-
     out.unindent();
     out << "private:\n";
     out.indent();
@@ -1056,33 +767,13 @@
     enterLeaveNamespace(out, false /* enter */);
 
     out << "\n#endif  // " << guard << "\n";
-
-    return OK;
 }
 
-status_t AST::generateCppSources(const std::string &outputPath) const {
+void AST::generateCppSource(Formatter& out) const {
     std::string baseName = getBaseName();
     const Interface *iface = getInterface();
 
-    std::string path = outputPath;
-    path.append(mCoordinator->convertPackageRootToPath(mPackage));
-    path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
-    path.append(baseName);
-
-    if (baseName != "types") {
-        path.append("All");
-    }
-
-    path.append(".cpp");
-
-    CHECK(Coordinator::MakeParentHierarchy(path));
-    FILE *file = fopen(path.c_str(), "w");
-
-    if (file == NULL) {
-        return -errno;
-    }
-
-    Formatter out(file);
+    const std::string klassName = baseName + (baseName == "types" ? "" : "All");
 
     out << "#define LOG_TAG \""
         << mPackage.string() << "::" << baseName
@@ -1116,9 +807,9 @@
     enterLeaveNamespace(out, true /* enter */);
     out << "\n";
 
-    status_t err = generateTypeSource(out, iface ? iface->localName() : "");
+    generateTypeSource(out, iface ? iface->localName() : "");
 
-    if (err == OK && iface) {
+    if (iface) {
         const Interface* iface = mRootScope.getInterface();
 
         // need to be put here, generateStubSource is using this.
@@ -1127,10 +818,10 @@
             << "::descriptor(\""
             << iface->fqName().string()
             << "\");\n\n";
-        out << "__attribute__((constructor))";
+        out << "__attribute__((constructor)) ";
         out << "static void static_constructor() {\n";
         out.indent([&] {
-            out << "::android::hardware::details::gBnConstructorMap.set("
+            out << "::android::hardware::details::getBnConstructorMap().set("
                 << iface->localName()
                 << "::descriptor,\n";
             out.indent(2, [&] {
@@ -1144,7 +835,7 @@
                 });
                 out << "});\n";
             });
-            out << "::android::hardware::details::gBsConstructorMap.set("
+            out << "::android::hardware::details::getBsConstructorMap().set("
                 << iface->localName()
                 << "::descriptor,\n";
             out.indent(2, [&] {
@@ -1165,32 +856,19 @@
         out << "__attribute__((destructor))";
         out << "static void static_destructor() {\n";
         out.indent([&] {
-            out << "::android::hardware::details::gBnConstructorMap.erase("
+            out << "::android::hardware::details::getBnConstructorMap().erase("
                 << iface->localName()
                 << "::descriptor);\n";
-            out << "::android::hardware::details::gBsConstructorMap.erase("
+            out << "::android::hardware::details::getBsConstructorMap().erase("
                 << iface->localName()
                 << "::descriptor);\n";
         });
         out << "};\n\n";
 
-        err = generateInterfaceSource(out);
-    }
-
-    if (err == OK && iface) {
-        err = generateProxySource(out, iface->fqName());
-    }
-
-    if (err == OK && iface) {
-        err = generateStubSource(out, iface);
-    }
-
-    if (err == OK && iface) {
-        err = generatePassthroughSource(out);
-    }
-
-    if (err == OK && iface) {
-        const Interface* iface = mRootScope.getInterface();
+        generateInterfaceSource(out);
+        generateProxySource(out, iface->fqName());
+        generateStubSource(out, iface);
+        generatePassthroughSource(out);
 
         if (isIBase()) {
             out << "// skipped getService, registerAsService, registerForNotifications\n";
@@ -1206,8 +884,6 @@
     out << "\n";
 
     enterLeaveNamespace(out, false /* enter */);
-
-    return err;
 }
 
 void AST::generateCheckNonNull(Formatter &out, const std::string &nonNull) {
@@ -1220,15 +896,12 @@
     }).endl().endl();
 }
 
-status_t AST::generateTypeSource(
-        Formatter &out, const std::string &ifaceName) const {
-    return mRootScope.emitTypeDefinitions(out, ifaceName);
+void AST::generateTypeSource(Formatter& out, const std::string& ifaceName) const {
+    mRootScope.emitTypeDefinitions(out, ifaceName);
 }
 
-void AST::declareCppReaderLocals(
-        Formatter &out,
-        const std::vector<TypedVar *> &args,
-        bool forResults) const {
+void AST::declareCppReaderLocals(Formatter& out, const std::vector<NamedReference<Type>*>& args,
+                                 bool forResults) const {
     if (args.empty()) {
         return;
     }
@@ -1245,14 +918,9 @@
     out << "\n";
 }
 
-void AST::emitCppReaderWriter(
-        Formatter &out,
-        const std::string &parcelObj,
-        bool parcelObjIsPointer,
-        const TypedVar *arg,
-        bool isReader,
-        Type::ErrorMode mode,
-        bool addPrefixToName) const {
+void AST::emitCppReaderWriter(Formatter& out, const std::string& parcelObj, bool parcelObjIsPointer,
+                              const NamedReference<Type>* arg, bool isReader, Type::ErrorMode mode,
+                              bool addPrefixToName) const {
     const Type &type = arg->type();
 
     type.emitReaderWriter(
@@ -1264,14 +932,10 @@
             mode);
 }
 
-void AST::emitCppResolveReferences(
-        Formatter &out,
-        const std::string &parcelObj,
-        bool parcelObjIsPointer,
-        const TypedVar *arg,
-        bool isReader,
-        Type::ErrorMode mode,
-        bool addPrefixToName) const {
+void AST::emitCppResolveReferences(Formatter& out, const std::string& parcelObj,
+                                   bool parcelObjIsPointer, const NamedReference<Type>* arg,
+                                   bool isReader, Type::ErrorMode mode,
+                                   bool addPrefixToName) const {
     const Type &type = arg->type();
     if(type.needsResolveReferences()) {
         type.emitResolveReferences(
@@ -1285,10 +949,8 @@
     }
 }
 
-status_t AST::generateProxyMethodSource(Formatter &out,
-                                        const std::string &klassName,
-                                        const Method *method,
-                                        const Interface *superInterface) const {
+void AST::generateProxyMethodSource(Formatter& out, const std::string& klassName,
+                                    const Method* method, const Interface* superInterface) const {
     method->generateCppSignature(out,
                                  klassName,
                                  true /* specify namespaces */);
@@ -1297,14 +959,12 @@
         out.block([&] {
             method->cppImpl(IMPL_PROXY, out);
         }).endl().endl();
-        return OK;
+        return;
     }
 
-    status_t err = OK;
-
     out.block([&] {
         const bool returnsValue = !method->results().empty();
-        const TypedVar *elidedReturn = method->canElideCallback();
+        const NamedReference<Type>* elidedReturn = method->canElideCallback();
 
         method->generateCppReturnType(out);
 
@@ -1335,15 +995,12 @@
 
         out << "return _hidl_out;\n";
     }).endl().endl();
-
-    return err;
 }
 
-status_t AST::generateStaticProxyMethodSource(Formatter &out,
-                                              const std::string &klassName,
-                                              const Method *method) const {
+void AST::generateStaticProxyMethodSource(Formatter& out, const std::string& klassName,
+                                          const Method* method) const {
     if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PROXY)) {
-        return OK;
+        return;
     }
 
     method->generateCppReturnType(out);
@@ -1372,7 +1029,7 @@
     out << "#endif // __ANDROID_DEBUGGABLE__\n";
 
     const bool returnsValue = !method->results().empty();
-    const TypedVar *elidedReturn = method->canElideCallback();
+    const NamedReference<Type>* elidedReturn = method->canElideCallback();
     if (returnsValue && elidedReturn == nullptr) {
         generateCheckNonNull(out, "_hidl_cb");
     }
@@ -1434,7 +1091,7 @@
         << " */, _hidl_data, &_hidl_reply";
 
     if (method->isOneway()) {
-        out << ", ::android::hardware::IBinder::FLAG_ONEWAY";
+        out << ", " << Interface::FLAG_ONEWAY << " /* oneway */";
     }
     out << ");\n";
 
@@ -1513,11 +1170,9 @@
 
     out.unindent();
     out << "}\n\n";
-    return OK;
 }
 
-status_t AST::generateProxySource(
-        Formatter &out, const FQName &fqName) const {
+void AST::generateProxySource(Formatter& out, const FQName& fqName) const {
     const std::string klassName = fqName.getInterfaceProxyName();
 
     out << klassName
@@ -1542,24 +1197,18 @@
     out.unindent();
     out << "}\n\n";
 
-    status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
-        return generateStaticProxyMethodSource(out, klassName, method);
-    }, false /* include parents */);
+    generateMethods(out,
+                    [&](const Method* method, const Interface*) {
+                        generateStaticProxyMethodSource(out, klassName, method);
+                    },
+                    false /* include parents */);
 
-    if (err != OK) {
-        return err;
-    }
-
-    err = generateMethods(out, [&](const Method *method, const Interface *superInterface) {
-        return generateProxyMethodSource(out, klassName, method, superInterface);
+    generateMethods(out, [&](const Method* method, const Interface* superInterface) {
+        generateProxyMethodSource(out, klassName, method, superInterface);
     });
-
-    return err;
 }
 
-status_t AST::generateStubSource(
-        Formatter &out,
-        const Interface *iface) const {
+void AST::generateStubSource(Formatter& out, const Interface* iface) const {
     const std::string interfaceName = iface->localName();
     const std::string klassName = iface->getStubName();
 
@@ -1625,24 +1274,22 @@
         out << "::android::hardware::details::gBnMap.eraseIfEqual(_hidl_mImpl.get(), this);\n";
     }).endl().endl();
 
-    status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
-        return generateStaticStubMethodSource(out, klassName, method);
-    }, false /* include parents */);
+    generateMethods(out,
+                    [&](const Method* method, const Interface*) {
+                        return generateStaticStubMethodSource(out, iface->fqName(), method);
+                    },
+                    false /* include parents */);
 
-    err = generateMethods(out, [&](const Method *method, const Interface *) {
+    generateMethods(out, [&](const Method* method, const Interface*) {
         if (!method->isHidlReserved() || !method->overridesCppImpl(IMPL_STUB_IMPL)) {
-            return OK;
+            return;
         }
         method->generateCppSignature(out, iface->getStubName());
         out << " ";
         out.block([&] {
             method->cppImpl(IMPL_STUB_IMPL, out);
         }).endl();
-        return OK;
     });
-    if (err != OK) {
-        return err;
-    }
 
     out << "::android::status_t " << klassName << "::onTransact(\n";
 
@@ -1665,6 +1312,9 @@
         const Method *method = tuple.method();
         const Interface *superInterface = tuple.interface();
 
+        if (!isIBase() && method->isHidlReserved()) {
+            continue;
+        }
         out << "case "
             << method->getSerialId()
             << " /* "
@@ -1673,15 +1323,12 @@
 
         out.indent();
 
-        out << "bool _hidl_is_oneway = _hidl_flags & ::android::hardware::IBinder::FLAG_ONEWAY;\n";
+        out << "bool _hidl_is_oneway = _hidl_flags & " << Interface::FLAG_ONEWAY
+            << " /* oneway */;\n";
         out << "if (_hidl_is_oneway != " << (method->isOneway() ? "true" : "false") << ") ";
         out.block([&] { out << "return ::android::UNKNOWN_ERROR;\n"; }).endl().endl();
 
-        status_t err = generateStubSourceForMethod(out, method, superInterface);
-
-        if (err != OK) {
-            return err;
-        }
+        generateStubSourceForMethod(out, method, superInterface);
 
         out.unindent();
         out << "}\n\n";
@@ -1726,19 +1373,14 @@
 
     out.unindent();
     out << "}\n\n";
-
-    return OK;
 }
 
-status_t AST::generateStubSourceForMethod(
-        Formatter &out,
-        const Method *method,
-        const Interface* superInterface) const {
-
+void AST::generateStubSourceForMethod(Formatter& out, const Method* method,
+                                      const Interface* superInterface) const {
     if (method->isHidlReserved() && method->overridesCppImpl(IMPL_STUB)) {
         method->cppImpl(IMPL_STUB, out);
         out << "break;\n";
-        return OK;
+        return;
     }
 
     out << "_hidl_err = "
@@ -1749,17 +1391,16 @@
         << method->name()
         << "(this, _hidl_data, _hidl_reply, _hidl_cb);\n";
     out << "break;\n";
-
-    return OK;
 }
 
-status_t AST::generateStaticStubMethodSource(Formatter &out,
-                                             const std::string &klassName,
-                                             const Method *method) const {
+void AST::generateStaticStubMethodSource(Formatter& out, const FQName& fqName,
+                                         const Method* method) const {
     if (method->isHidlReserved() && method->overridesCppImpl(IMPL_STUB)) {
-        return OK;
+        return;
     }
 
+    const std::string& klassName = fqName.getInterfaceStubName();
+
     out << "::android::status_t " << klassName << "::_hidl_" << method->name() << "(\n";
 
     out.indent();
@@ -1821,14 +1462,14 @@
             method);
 
     const bool returnsValue = !method->results().empty();
-    const TypedVar *elidedReturn = method->canElideCallback();
+    const NamedReference<Type>* elidedReturn = method->canElideCallback();
 
     std::string callee;
 
     if (method->isHidlReserved() && method->overridesCppImpl(IMPL_STUB_IMPL)) {
         callee = "_hidl_this";
     } else {
-        callee = "static_cast<" + klassName + "*>(_hidl_this)->_hidl_mImpl";
+        callee = "static_cast<" + fqName.getInterfaceName() + "*>(_hidl_this->getImpl().get())";
     }
 
     if (elidedReturn != nullptr) {
@@ -1973,14 +1614,12 @@
     out << "return _hidl_err;\n";
     out.unindent();
     out << "}\n\n";
-
-    return OK;
 }
 
-status_t AST::generatePassthroughHeader(const std::string &outputPath) const {
+void AST::generatePassthroughHeader(Formatter& out) const {
     if (!AST::isInterface()) {
         // types.hal does not get a stub header.
-        return OK;
+        return;
     }
 
     const Interface* iface = mRootScope.getInterface();
@@ -1990,21 +1629,6 @@
 
     bool supportOneway = iface->hasOnewayMethods();
 
-    std::string path = outputPath;
-    path.append(mCoordinator->convertPackageRootToPath(mPackage));
-    path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
-    path.append(klassName);
-    path.append(".h");
-
-    CHECK(Coordinator::MakeParentHierarchy(path));
-    FILE *file = fopen(path.c_str(), "w");
-
-    if (file == NULL) {
-        return -errno;
-    }
-
-    Formatter out(file);
-
     const std::string guard = makeHeaderGuard(klassName);
 
     out << "#ifndef " << guard << "\n";
@@ -2043,15 +1667,12 @@
 
     out.endl();
     generateTemplatizationLink(out);
+    generateCppTag(out, "android::hardware::details::bs_tag");
 
-    status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
-        return generatePassthroughMethod(out, method);
+    generateMethods(out, [&](const Method* method, const Interface*) {
+        generatePassthroughMethod(out, method);
     });
 
-    if (err != OK) {
-        return err;
-    }
-
     out.unindent();
     out << "private:\n";
     out.indent();
@@ -2073,17 +1694,15 @@
     enterLeaveNamespace(out, false /* enter */);
 
     out << "\n#endif  // " << guard << "\n";
-
-    return OK;
 }
 
-status_t AST::generateInterfaceSource(Formatter &out) const {
+void AST::generateInterfaceSource(Formatter& out) const {
     const Interface* iface = mRootScope.getInterface();
 
     // generate castFrom functions
     std::string childTypeResult = iface->getCppResultType();
 
-    status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
+    generateMethods(out, [&](const Method* method, const Interface*) {
         bool reserved = method->isHidlReserved();
 
         if (!reserved) {
@@ -2098,11 +1717,8 @@
 
         out << "\n";
 
-        return OK;
+        return;
     });
-    if (err != OK) {
-        return err;
-    }
 
     for (const Interface *superType : iface->typeChain()) {
         out << "// static \n::android::hardware::Return<"
@@ -2134,11 +1750,9 @@
         out.unindent();
         out << "}\n\n";
     }
-
-    return OK;
 }
 
-status_t AST::generatePassthroughSource(Formatter &out) const {
+void AST::generatePassthroughSource(Formatter& out) const {
     const Interface* iface = mRootScope.getInterface();
 
     const std::string klassName = iface->getPassthroughName();
@@ -2185,8 +1799,6 @@
 
 
     }
-
-    return OK;
 }
 
 void AST::generateCppAtraceCall(Formatter &out,
@@ -2222,7 +1834,7 @@
         }
         default:
         {
-            LOG(FATAL) << "Unsupported instrumentation event: " << event;
+            CHECK(false) << "Unsupported instrumentation event: " << event;
         }
     }
 }
@@ -2304,7 +1916,7 @@
         }
         default:
         {
-            LOG(FATAL) << "Unsupported instrumentation event: " << event;
+            CHECK(false) << "Unsupported instrumentation event: " << event;
         }
     }
 
diff --git a/generateCppAdapter.cpp b/generateCppAdapter.cpp
new file mode 100644
index 0000000..1fbfb57
--- /dev/null
+++ b/generateCppAdapter.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AST.h"
+
+#include "Coordinator.h"
+#include "EnumType.h"
+#include "HidlTypeAssertion.h"
+#include "Interface.h"
+#include "Method.h"
+#include "Reference.h"
+#include "ScalarType.h"
+#include "Scope.h"
+
+#include <android-base/logging.h>
+#include <hidl-util/Formatter.h>
+#include <hidl-util/StringHelper.h>
+#include <algorithm>
+#include <string>
+#include <vector>
+
+namespace android {
+
+void AST::generateCppAdapterHeader(Formatter& out) const {
+    const std::string klassName = AST::isInterface() ? getInterface()->getAdapterName() : "Atypes";
+    const std::string guard = makeHeaderGuard(klassName, true /* indicateGenerated */);
+
+    out << "#ifndef " << guard << "\n";
+    out << "#define " << guard << "\n\n";
+
+    if (AST::isInterface()) {
+        generateCppPackageInclude(out, mPackage, getInterface()->localName());
+
+        enterLeaveNamespace(out, true /* enter */);
+        out.endl();
+
+        const std::string mockName = getInterface()->fqName().cppName();
+
+        out << "class " << klassName << " : public " << mockName << " ";
+        out.block([&] {
+            out << "public:\n";
+            out << "typedef " << mockName << " Pure;\n";
+
+            out << klassName << "(::android::sp<" << mockName << "> impl);\n";
+
+            generateMethods(out, [&](const Method* method, const Interface* /* interface */) {
+                if (method->isHidlReserved()) {
+                    return;
+                }
+
+                out << "virtual ";
+                method->generateCppSignature(out);
+                out << " override;\n";
+            });
+            out << "private:\n";
+            out << "::android::sp<" << mockName << "> mImpl;\n";
+
+        }) << ";\n\n";
+
+        enterLeaveNamespace(out, false /* enter */);
+    } else {
+        out << "// no adapters for types.hal\n";
+    }
+
+    out << "#endif // " << guard << "\n";
+}
+
+void AST::generateCppAdapterSource(Formatter& out) const {
+    const std::string klassName = AST::isInterface() ? getInterface()->getAdapterName() : "Atypes";
+
+    generateCppPackageInclude(out, mPackage, klassName);
+
+    if (AST::isInterface()) {
+        out << "#include <hidladapter/HidlBinderAdapter.h>\n";
+        generateCppPackageInclude(out, mPackage, getInterface()->localName());
+
+        std::set<FQName> allImportedNames;
+        getAllImportedNames(&allImportedNames);
+        for (const auto& item : allImportedNames) {
+            if (item.name() == "types") {
+                continue;
+            }
+            generateCppPackageInclude(out, item, item.getInterfaceAdapterName());
+        }
+
+        out.endl();
+
+        enterLeaveNamespace(out, true /* enter */);
+        out.endl();
+
+        const std::string mockName = getInterface()->fqName().cppName();
+
+        out << klassName << "::" << klassName << "(::android::sp<" << mockName
+            << "> impl) : mImpl(impl) {}";
+
+        generateMethods(out, [&](const Method* method, const Interface* /* interface */) {
+            generateAdapterMethod(out, method);
+        });
+
+        enterLeaveNamespace(out, false /* enter */);
+        out.endl();
+    } else {
+        out << "// no adapters for types.hal\n";
+    }
+}
+
+void AST::generateAdapterMethod(Formatter& out, const Method* method) const {
+    if (method->isHidlReserved()) {
+        return;
+    }
+
+    const auto adapt = [](Formatter& out, const std::string& var, const Type* type) {
+        if (!type->isInterface()) {
+            out << var;
+            return;
+        }
+
+        // TODO(b/66900959): if we are creating the adapter for a 1.1 IFoo
+        // and we are using a method that takes/returns a 1.0 Callback, but
+        // there exists a 1.1 Callback (or other subclass that is depended
+        // on by this module), then wrap with the adapter subclass adapter
+        // IFF that callback is a subclass. However, if the callback
+        // is 1.0 ICallback, then wrap with a 1.0 adapter.
+
+        const Interface* interface = static_cast<const Interface*>(type);
+        out << "static_cast<::android::sp<" << interface->fqName().cppName() << ">>("
+            << interface->fqName().cppName() << "::castFrom("
+            << "::android::hardware::details::adaptWithDefault("
+            << "static_cast<::android::sp<" << interface->fqName().cppName() << ">>(" << var
+            << "), [&] { return new " << interface->fqName().getInterfaceAdapterFqName().cppName()
+            << "(" << var << "); })))";
+    };
+
+    const std::string klassName = getInterface()->getAdapterName();
+
+    method->generateCppSignature(out, klassName);
+    out.block([&] {
+         bool hasCallback = !method->canElideCallback() && !method->results().empty();
+
+         if (hasCallback) {
+             out << method->name() << "_cb _hidl_cb_wrapped = [&](";
+             method->emitCppResultSignature(out);
+             out << ") ";
+             out.block([&] {
+                 out << "return _hidl_cb(\n";
+                 out.indent([&]() {
+                     out.join(method->results().begin(), method->results().end(), ",\n",
+                              [&](auto arg) { adapt(out, arg->name(), arg->get()); });
+                 });
+                 out << ");\n";
+             });
+             out << ";\n";
+         }
+
+         out << "auto _hidl_out = mImpl->" << method->name() << "(\n";
+         out.indent([&]() {
+             out.join(method->args().begin(), method->args().end(), ",\n",
+                      [&](auto arg) { adapt(out, arg->name(), arg->get()); });
+             if (hasCallback) {
+                 if (!method->args().empty()) {
+                     out << ",\n";
+                 }
+                 out << "_hidl_cb_wrapped";
+             }
+         });
+         out << ");\n";
+
+         const auto elidedCallback = method->canElideCallback();
+         if (elidedCallback) {
+             out.sIf("!_hidl_out.isOkUnchecked()", [&] { out << "return _hidl_out;\n"; });
+             out << "return ";
+             adapt(out, "_hidl_out", elidedCallback->get());
+             out << ";\n";
+         } else {
+             out << "return _hidl_out;\n";
+         }
+     }).endl();
+}
+
+}  // namespace android
diff --git a/generateCppImpl.cpp b/generateCppImpl.cpp
index ae95590..18fac6b 100644
--- a/generateCppImpl.cpp
+++ b/generateCppImpl.cpp
@@ -20,6 +20,7 @@
 #include "EnumType.h"
 #include "Interface.h"
 #include "Method.h"
+#include "Reference.h"
 #include "ScalarType.h"
 #include "Scope.h"
 
@@ -32,27 +33,15 @@
 
 namespace android {
 
-status_t AST::generateCppImpl(const std::string &outputPath) const {
-    status_t err = generateStubImplHeader(outputPath);
-
-    if (err == OK) {
-        err = generateStubImplSource(outputPath);
-    }
-
-    return err;
-}
-
 void AST::generateFetchSymbol(Formatter &out, const std::string& ifaceName) const {
     out << "HIDL_FETCH_" << ifaceName;
 }
 
-status_t AST::generateStubImplMethod(Formatter &out,
-                                     const std::string &className,
-                                     const Method *method) const {
-
+void AST::generateStubImplMethod(Formatter& out, const std::string& className,
+                                 const Method* method) const {
     // ignore HIDL reserved methods -- implemented in IFoo already.
     if (method->isHidlReserved()) {
-        return OK;
+        return;
     }
 
     method->generateCppSignature(out, className, false /* specifyNamespaces */);
@@ -62,7 +51,7 @@
     out.indent();
     out << "// TODO implement\n";
 
-    const TypedVar *elidedReturn = method->canElideCallback();
+    const NamedReference<Type>* elidedReturn = method->canElideCallback();
 
     if (elidedReturn == nullptr) {
         out << "return Void();\n";
@@ -76,31 +65,18 @@
 
     out << "}\n\n";
 
-    return OK;
+    return;
 }
 
-status_t AST::generateStubImplHeader(const std::string &outputPath) const {
+void AST::generateCppImplHeader(Formatter& out) const {
     if (!AST::isInterface()) {
         // types.hal does not get a stub header.
-        return OK;
+        return;
     }
 
     const Interface* iface = mRootScope.getInterface();
     const std::string baseName = iface->getBaseName();
 
-    std::string path = outputPath;
-    path.append(baseName);
-    path.append(".h");
-
-    CHECK(Coordinator::MakeParentHierarchy(path));
-    FILE *file = fopen(path.c_str(), "w");
-
-    if (file == NULL) {
-        return -errno;
-    }
-
-    Formatter out(file);
-
     const std::string guard = makeHeaderGuard(baseName, false /* indicateGenerated */);
 
     out << "#ifndef " << guard << "\n";
@@ -132,21 +108,16 @@
 
     out.indent();
 
-    status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
+    generateMethods(out, [&](const Method* method, const Interface*) {
         // ignore HIDL reserved methods -- implemented in IFoo already.
         if (method->isHidlReserved()) {
-            return OK;
+            return;
         }
         method->generateCppSignature(out, "" /* className */,
                 false /* specifyNamespaces */);
         out << " override;\n";
-        return OK;
     });
 
-    if (err != OK) {
-        return err;
-    }
-
     out.unindent();
 
     out << "};\n\n";
@@ -162,45 +133,26 @@
     enterLeaveNamespace(out, false /* leave */);
 
     out << "\n#endif  // " << guard << "\n";
-
-    return OK;
 }
 
-status_t AST::generateStubImplSource(const std::string &outputPath) const {
+void AST::generateCppImplSource(Formatter& out) const {
     if (!AST::isInterface()) {
         // types.hal does not get a stub header.
-        return OK;
+        return;
     }
 
     const Interface* iface = mRootScope.getInterface();
     const std::string baseName = iface->getBaseName();
 
-    std::string path = outputPath;
-    path.append(baseName);
-    path.append(".cpp");
-
-    CHECK(Coordinator::MakeParentHierarchy(path));
-    FILE *file = fopen(path.c_str(), "w");
-
-    if (file == NULL) {
-        return -errno;
-    }
-
-    Formatter out(file);
-
     out << "#include \"" << baseName << ".h\"\n\n";
 
     enterLeaveNamespace(out, true /* enter */);
     out << "namespace implementation {\n\n";
 
-    status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
-        return generateStubImplMethod(out, baseName, method);
+    generateMethods(out, [&](const Method* method, const Interface*) {
+        generateStubImplMethod(out, baseName, method);
     });
 
-    if (err != OK) {
-        return err;
-    }
-
     out.setLinePrefix("//");
     out << iface->localName()
         << "* ";
@@ -214,8 +166,6 @@
 
     out << "}  // namespace implementation\n";
     enterLeaveNamespace(out, false /* leave */);
-
-    return OK;
 }
 
 }  // namespace android
diff --git a/generateJava.cpp b/generateJava.cpp
index f463b7f..bdbe25a 100644
--- a/generateJava.cpp
+++ b/generateJava.cpp
@@ -19,6 +19,7 @@
 #include "Coordinator.h"
 #include "Interface.h"
 #include "Method.h"
+#include "Reference.h"
 #include "Scope.h"
 
 #include <hidl-util/Formatter.h>
@@ -26,12 +27,9 @@
 
 namespace android {
 
-void AST::emitJavaReaderWriter(
-        Formatter &out,
-        const std::string &parcelObj,
-        const TypedVar *arg,
-        bool isReader,
-        bool addPrefixToName) const {
+void AST::emitJavaReaderWriter(Formatter& out, const std::string& parcelObj,
+                               const NamedReference<Type>* arg, bool isReader,
+                               bool addPrefixToName) const {
     if (isReader) {
         out << arg->type().getJavaType()
             << " "
@@ -45,94 +43,81 @@
             isReader);
 }
 
-status_t AST::generateJavaTypes(
-        const std::string &outputPath, const std::string &limitToType) const {
+void AST::generateJavaTypes(Formatter& out, const std::string& limitToType) const {
     // Splits types.hal up into one java file per declared type.
+    CHECK(!limitToType.empty()) << getFilename();
 
     for (const auto& type : mRootScope.getSubTypes()) {
         std::string typeName = type->localName();
 
-        if (type->isTypeDef()) {
-            continue;
-        }
-
-        if (!limitToType.empty() && typeName != limitToType) {
-            continue;
-        }
-
-        std::string path = outputPath;
-        path.append(mCoordinator->convertPackageRootToPath(mPackage));
-        path.append(mCoordinator->getPackagePath(mPackage, true /* relative */,
-                true /* sanitized */));
-        path.append(typeName);
-        path.append(".java");
-
-        CHECK(Coordinator::MakeParentHierarchy(path)) << path;
-        FILE *file = fopen(path.c_str(), "w");
-
-        if (file == NULL) {
-            return -errno;
-        }
-
-        Formatter out(file);
+        if (type->isTypeDef()) continue;
+        if (typeName != limitToType) continue;
 
         std::vector<std::string> packageComponents;
         getPackageAndVersionComponents(
                 &packageComponents, true /* cpp_compatible */);
 
-        out << "package " << mPackage.javaPackage() << ";\n\n";
+        out << "package " << mPackage.javaPackage() << ";\n\n\n";
 
-        out << "\n";
-
-        status_t err =
-            type->emitJavaTypeDeclarations(out, true /* atTopLevel */);
-
-        if (err != OK) {
-            return err;
-        }
+        type->emitJavaTypeDeclarations(out, true /* atTopLevel */);
+        return;
     }
 
-    return OK;
+    CHECK(false) << "generateJavaTypes could not find limitToType type";
 }
 
-status_t AST::generateJava(
-        const std::string &outputPath, const std::string &limitToType) const {
-    if (!isJavaCompatible()) {
-        fprintf(stderr,
-                "ERROR: This interface is not Java compatible. The Java backend"
-                " does NOT support union types nor native handles. "
-                "In addition, vectors of arrays are limited to at most "
-                "one-dimensional arrays and vectors of {vectors,interfaces} are"
-                " not supported.\n");
-
-        return UNKNOWN_ERROR;
+void emitGetService(
+        Formatter& out,
+        const std::string& ifaceName,
+        const std::string& fqName,
+        bool isRetry) {
+    out << "public static "
+        << ifaceName
+        << " getService(String serviceName";
+    if (isRetry) {
+        out << ", boolean retry";
     }
+    out << ") throws android.os.RemoteException ";
+    out.block([&] {
+        out << "return "
+            << ifaceName
+            << ".asInterface(android.os.HwBinder.getService(\""
+            << fqName
+            << "\", serviceName";
+        if (isRetry) {
+            out << ", retry";
+        }
+        out << "));\n";
+    }).endl().endl();
+
+    out << "public static "
+        << ifaceName
+        << " getService(";
+    if (isRetry) {
+        out << "boolean retry";
+    }
+    out << ") throws android.os.RemoteException ";
+    out.block([&] {
+        out << "return getService(\"default\"";
+        if (isRetry) {
+            out << ", retry";
+        }
+        out <<");\n";
+    }).endl().endl();
+}
+
+void AST::generateJava(Formatter& out, const std::string& limitToType) const {
+    CHECK(isJavaCompatible()) << getFilename();
 
     if (!AST::isInterface()) {
-        return generateJavaTypes(outputPath, limitToType);
+        generateJavaTypes(out, limitToType);
+        return;
     }
 
     const Interface* iface = mRootScope.getInterface();
-    std::string ifaceName = iface->localName();
-
+    const std::string ifaceName = iface->localName();
     const std::string baseName = iface->getBaseName();
 
-    std::string path = outputPath;
-    path.append(mCoordinator->convertPackageRootToPath(mPackage));
-    path.append(mCoordinator->getPackagePath(mPackage, true /* relative */,
-            true /* sanitized */));
-    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 */);
@@ -225,43 +210,10 @@
 
     out << "@Override\npublic android.os.IHwBinder asBinder();\n\n";
 
-    out << "public static "
-        << ifaceName
-        << " getService(String serviceName) throws android.os.RemoteException {\n";
+    emitGetService(out, ifaceName, iface->fqName().string(), true /* isRetry */);
+    emitGetService(out, ifaceName, iface->fqName().string(), false /* isRetry */);
 
-    out.indent();
-
-    out << "return "
-        << ifaceName
-        << ".asInterface(android.os.HwBinder.getService(\""
-        << iface->fqName().string()
-        << "\",serviceName));\n";
-
-    out.unindent();
-
-    out << "}\n\n";
-
-    out << "public static "
-        << ifaceName
-        << " getService() throws android.os.RemoteException {\n";
-
-    out.indent();
-
-    out << "return "
-        << ifaceName
-        << ".asInterface(android.os.HwBinder.getService(\""
-        << iface->fqName().string()
-        << "\",\"default\"));\n";
-
-    out.unindent();
-
-    out << "}\n\n";
-
-    status_t err = emitJavaTypeDeclarations(out);
-
-    if (err != OK) {
-        return err;
-    }
+    emitJavaTypeDeclarations(out);
 
     for (const auto &method : iface->methods()) {
         if (method->isHiddenFromJava()) {
@@ -285,6 +237,8 @@
             out << "}\n\n";
         }
 
+        method->emitDocComment(out);
+
         if (returnsValue && !needsCallback) {
             out << method->results()[0]->type().getJavaType();
         } else {
@@ -342,6 +296,18 @@
             << ifaceName << ".kInterfaceName + \"]@Proxy\";\n";
     }).endl().endl();
 
+    // Equals when internal binder object is equal (even if the interface Proxy object
+    // itself is different). This is similar to interfacesEqual in C++.
+    out << "@Override\npublic final boolean equals(java.lang.Object other) ";
+    out.block([&] {
+        out << "return android.os.HidlSupport.interfacesEqual(this, other);\n";
+    }).endl().endl();
+
+    out << "@Override\npublic final int hashCode() ";
+    out.block([&] {
+        out << "return this.asBinder().hashCode();\n";
+    }).endl().endl();
+
     const Interface *prevInterface = nullptr;
     for (const auto &tuple : iface->allMethodsFromRoot()) {
         const Method *method = tuple.method();
@@ -417,7 +383,7 @@
                 << " */, _hidl_request, _hidl_reply, ";
 
             if (method->isOneway()) {
-                out << "android.os.IHwBinder.FLAG_ONEWAY";
+                out << Interface::FLAG_ONEWAY << " /* oneway */";
             } else {
                 out << "0 /* flags */";
             }
@@ -484,6 +450,8 @@
 
     out << "@Override\npublic android.os.IHwBinder asBinder() {\n";
     out.indent();
+    // If we change this behavior in the future and asBinder does not return "this",
+    // equals and hashCode should also be overridden.
     out << "return this;\n";
     out.unindent();
     out << "}\n\n";
@@ -567,6 +535,15 @@
 
         out.indent();
 
+        out << "boolean _hidl_is_oneway = (_hidl_flags & " << Interface::FLAG_ONEWAY
+            << " /* oneway */) != 0\n;";
+        out << "if (_hidl_is_oneway != " << (method->isOneway() ? "true" : "false") << ") ";
+        out.block([&] {
+            out << "_hidl_reply.writeStatus(" << UNKNOWN_ERROR << ");\n";
+            out << "_hidl_reply.send();\n";
+            out << "break;\n";
+        });
+
         if (method->isHidlReserved() && method->overridesJavaImpl(IMPL_STUB)) {
             method->javaImpl(IMPL_STUB, out);
             out.unindent();
@@ -602,7 +579,7 @@
         }
 
         if (!needsCallback && returnsValue) {
-            const TypedVar *returnArg = method->results()[0];
+            const NamedReference<Type>* returnArg = method->results()[0];
 
             out << returnArg->type().getJavaType()
                 << " _hidl_out_"
@@ -663,7 +640,7 @@
             out << "_hidl_reply.writeStatus(android.os.HwParcel.STATUS_SUCCESS);\n";
 
             if (returnsValue) {
-                const TypedVar *returnArg = method->results()[0];
+                const NamedReference<Type>* returnArg = method->results()[0];
 
                 emitJavaReaderWriter(
                         out,
@@ -692,12 +669,10 @@
 
     out.unindent();
     out << "}\n";
-
-    return OK;
 }
 
-status_t AST::emitJavaTypeDeclarations(Formatter &out) const {
-    return mRootScope.emitJavaTypeDeclarations(out, false /* atTopLevel */);
+void AST::emitJavaTypeDeclarations(Formatter& out) const {
+    mRootScope.emitJavaTypeDeclarations(out, false /* atTopLevel */);
 }
 
 }  // namespace android
diff --git a/generateVts.cpp b/generateVts.cpp
index 5fd8670..8a37ae3 100644
--- a/generateVts.cpp
+++ b/generateVts.cpp
@@ -29,7 +29,7 @@
 
 namespace android {
 
-status_t AST::emitVtsTypeDeclarations(Formatter &out) const {
+void AST::emitVtsTypeDeclarations(Formatter& out) const {
     if (AST::isInterface()) {
         const Interface* iface = mRootScope.getInterface();
         return iface->emitVtsAttributeDeclaration(out);
@@ -42,36 +42,16 @@
         }
         out << "attribute: {\n";
         out.indent();
-        status_t status = type->emitVtsTypeDeclarations(out);
-        if (status != OK) {
-            return status;
-        }
+        type->emitVtsTypeDeclarations(out);
         out.unindent();
         out << "}\n\n";
     }
-
-    return OK;
 }
 
-status_t AST::generateVts(const std::string &outputPath) const {
+void AST::generateVts(Formatter& out) const {
     std::string baseName = AST::getBaseName();
     const Interface *iface = AST::getInterface();
 
-    std::string path = outputPath;
-    path.append(mCoordinator->convertPackageRootToPath(mPackage));
-    path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
-    path.append(baseName);
-    path.append(".vts");
-
-    CHECK(Coordinator::MakeParentHierarchy(path));
-    FILE *file = fopen(path.c_str(), "w");
-
-    if (file == NULL) {
-        return -errno;
-    }
-
-    Formatter out(file);
-
     out << "component_class: HAL_HIDL\n";
     out << "component_type_version: " << mPackage.version()
         << "\n";
@@ -101,28 +81,19 @@
         std::vector<const Interface *> chain = iface->typeChain();
 
         // Generate all the attribute declarations first.
-        status_t status = emitVtsTypeDeclarations(out);
-        if (status != OK) {
-            return status;
-        }
+        emitVtsTypeDeclarations(out);
+
         // Generate all the method declarations.
         for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
             const Interface *superInterface = *it;
-            status_t status = superInterface->emitVtsMethodDeclaration(out);
-            if (status != OK) {
-                return status;
-            }
+            superInterface->emitVtsMethodDeclaration(out);
         }
 
         out.unindent();
         out << "}\n";
     } else {
-        status_t status = emitVtsTypeDeclarations(out);
-        if (status != OK) {
-            return status;
-        }
+        emitVtsTypeDeclarations(out);
     }
-    return OK;
 }
 
 }  // namespace android
diff --git a/hidl-gen_l.h b/hidl-gen_l.h
new file mode 100644
index 0000000..52237e4
--- /dev/null
+++ b/hidl-gen_l.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <AST.h>
+
+#include <utils/Errors.h>
+
+#include <stdio.h>
+
+namespace android {
+
+// entry-point for file parsing
+// - contents of file are added to the AST
+// - expects file to already be open
+status_t parseFile(AST* ast, std::unique_ptr<FILE, std::function<void(FILE*)>> file);
+
+}  // namespace android
\ No newline at end of file
diff --git a/hidl-gen_l.ll b/hidl-gen_l.ll
index 13b69bf..c94df01 100644
--- a/hidl-gen_l.ll
+++ b/hidl-gen_l.ll
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-D			[0-9]
-L			[a-zA-Z_]
-H			[a-fA-F0-9]
-E			[Ee][+-]?{D}+
-FS			(f|F|l|L)
-IS			(u|U|l|L)*
+D                   [0-9]
+L                   [a-zA-Z_]
+H                   [a-fA-F0-9]
+E                   [Ee][+-]?{D}+
+FS                  (f|F|l|L)
+IS                  (u|U|l|L)*
 
-COMPONENT               {L}({L}|{D})*
-DOT                     [.]
-AT                      [@]
-VERSION                 {AT}{D}+{DOT}{D}+
-FQNAME                  ({COMPONENT}|{VERSION})(({DOT}|":"+){COMPONENT}|{VERSION})*
+COMPONENT           {L}({L}|{D})*
+DOT                 [.]
+AT                  [@]
+VERSION             {AT}{D}+{DOT}{D}+
+FQNAME              ({COMPONENT}|{VERSION})(({DOT}|":"+){COMPONENT}|{VERSION})*
 
 %{
 
@@ -35,12 +35,14 @@
 #include "CompoundType.h"
 #include "ConstantExpression.h"
 #include "DeathRecipientType.h"
+#include "DocComment.h"
 #include "EnumType.h"
 #include "HandleType.h"
 #include "MemoryType.h"
 #include "Method.h"
 #include "PointerType.h"
 #include "ScalarType.h"
+#include "Scope.h"
 #include "StringType.h"
 #include "VectorType.h"
 #include "RefType.h"
@@ -53,17 +55,23 @@
 using namespace android;
 using token = yy::parser::token;
 
-#define SCALAR_TYPE(kind)                                       \
-    do {                                                        \
-        yylval->type = new ScalarType(ScalarType::kind);        \
-        return token::TYPE;                                   \
-    } while (0)
+static std::string gCurrentComment;
+
+#define SCALAR_TYPE(kind)                                        \
+    {                                                            \
+        yylval->type = new ScalarType(ScalarType::kind, *scope); \
+        return token::TYPE;                                      \
+    }
+
+#define YY_DECL int yylex(YYSTYPE* yylval_param, YYLTYPE* yylloc_param,  \
+    yyscan_t yyscanner, android::Scope** const scope)
 
 #define YY_USER_ACTION yylloc->step(); yylloc->columns(yyleng);
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wunused-parameter"
 #pragma clang diagnostic ignored "-Wdeprecated-register"
+#pragma clang diagnostic ignored "-Wregister"
 
 %}
 
@@ -76,127 +84,135 @@
 %option bison-locations
 
 %x COMMENT_STATE
+%x DOC_COMMENT_STATE
 
 %%
 
-"/*"                 { BEGIN(COMMENT_STATE); }
-<COMMENT_STATE>"*/"  { BEGIN(INITIAL); }
-<COMMENT_STATE>[\n]  { yylloc->lines(); }
-<COMMENT_STATE>.     { }
+"/**"                       { gCurrentComment.clear(); BEGIN(DOC_COMMENT_STATE); }
+<DOC_COMMENT_STATE>"*/"     {
+                                BEGIN(INITIAL);
+                                yylval->docComment = new DocComment(gCurrentComment);
+                                return token::DOC_COMMENT;
+                            }
+<DOC_COMMENT_STATE>[^*\n]*                          { gCurrentComment += yytext; }
+<DOC_COMMENT_STATE>[\n]                             { gCurrentComment += yytext; yylloc->lines(); }
+<DOC_COMMENT_STATE>[*]                              { gCurrentComment += yytext; }
 
-"//"[^\r\n]*         { /* skip C++ style comment */ }
+"/*"                        { BEGIN(COMMENT_STATE); }
+<COMMENT_STATE>"*/"         { BEGIN(INITIAL); }
+<COMMENT_STATE>[\n]         { yylloc->lines(); }
+<COMMENT_STATE>.            { }
 
-"enum"			{ return token::ENUM; }
-"extends"		{ return token::EXTENDS; }
-"generates"		{ return token::GENERATES; }
-"import"		{ return token::IMPORT; }
-"interface"		{ return token::INTERFACE; }
-"package"		{ return token::PACKAGE; }
-"struct"		{ return token::STRUCT; }
-"typedef"		{ return token::TYPEDEF; }
-"union"			{ return token::UNION; }
-"bitfield"		{ yylval->templatedType = new BitFieldType; return token::TEMPLATED; }
-"vec"			{ yylval->templatedType = new VectorType; return token::TEMPLATED; }
-"ref"			{ yylval->templatedType = new RefType; return token::TEMPLATED; }
-"oneway"		{ return token::ONEWAY; }
+"//"[^\r\n]*        { /* skip C++ style comment */ }
 
-"bool"			{ SCALAR_TYPE(KIND_BOOL); }
-"int8_t"		{ SCALAR_TYPE(KIND_INT8); }
-"uint8_t"		{ SCALAR_TYPE(KIND_UINT8); }
-"int16_t"		{ SCALAR_TYPE(KIND_INT16); }
-"uint16_t"		{ SCALAR_TYPE(KIND_UINT16); }
-"int32_t"		{ SCALAR_TYPE(KIND_INT32); }
-"uint32_t"		{ SCALAR_TYPE(KIND_UINT32); }
-"int64_t"		{ SCALAR_TYPE(KIND_INT64); }
-"uint64_t"		{ SCALAR_TYPE(KIND_UINT64); }
-"float"			{ SCALAR_TYPE(KIND_FLOAT); }
-"double"		{ SCALAR_TYPE(KIND_DOUBLE); }
+"enum"              { return token::ENUM; }
+"extends"           { return token::EXTENDS; }
+"generates"         { return token::GENERATES; }
+"import"            { return token::IMPORT; }
+"interface"         { return token::INTERFACE; }
+"package"           { return token::PACKAGE; }
+"struct"            { return token::STRUCT; }
+"typedef"           { return token::TYPEDEF; }
+"union"             { return token::UNION; }
+"bitfield"          { yylval->templatedType = new BitFieldType(*scope); return token::TEMPLATED; }
+"vec"               { yylval->templatedType = new VectorType(*scope); return token::TEMPLATED; }
+"ref"               { yylval->templatedType = new RefType(*scope); return token::TEMPLATED; }
+"oneway"            { return token::ONEWAY; }
 
-"death_recipient"	{ yylval->type = new DeathRecipientType; return token::TYPE; }
-"handle"		{ yylval->type = new HandleType; return token::TYPE; }
-"memory"		{ yylval->type = new MemoryType; return token::TYPE; }
-"pointer"		{ yylval->type = new PointerType; return token::TYPE; }
-"string"		{ yylval->type = new StringType; return token::TYPE; }
+"bool"              { SCALAR_TYPE(KIND_BOOL); }
+"int8_t"            { SCALAR_TYPE(KIND_INT8); }
+"uint8_t"           { SCALAR_TYPE(KIND_UINT8); }
+"int16_t"           { SCALAR_TYPE(KIND_INT16); }
+"uint16_t"          { SCALAR_TYPE(KIND_UINT16); }
+"int32_t"           { SCALAR_TYPE(KIND_INT32); }
+"uint32_t"          { SCALAR_TYPE(KIND_UINT32); }
+"int64_t"           { SCALAR_TYPE(KIND_INT64); }
+"uint64_t"          { SCALAR_TYPE(KIND_UINT64); }
+"float"             { SCALAR_TYPE(KIND_FLOAT); }
+"double"            { SCALAR_TYPE(KIND_DOUBLE); }
 
-"fmq_sync" { yylval->type = new FmqType("::android::hardware", "MQDescriptorSync"); return token::TEMPLATED; }
-"fmq_unsync" { yylval->type = new FmqType("::android::hardware", "MQDescriptorUnsync"); return token::TEMPLATED; }
+"death_recipient"   { yylval->type = new DeathRecipientType(*scope); return token::TYPE; }
+"handle"            { yylval->type = new HandleType(*scope); return token::TYPE; }
+"memory"            { yylval->type = new MemoryType(*scope); return token::TYPE; }
+"pointer"           { yylval->type = new PointerType(*scope); return token::TYPE; }
+"string"            { yylval->type = new StringType(*scope); return token::TYPE; }
 
-"("			{ return('('); }
-")"			{ return(')'); }
-"<"			{ return('<'); }
-">"			{ return('>'); }
-"{"			{ return('{'); }
-"}"			{ return('}'); }
-"["			{ return('['); }
-"]"			{ return(']'); }
-":"			{ return(':'); }
-";"			{ return(';'); }
-","			{ return(','); }
-"."			{ return('.'); }
-"="			{ return('='); }
-"+"			{ return('+'); }
-"-"			{ return('-'); }
-"*"			{ return('*'); }
-"/"			{ return('/'); }
-"%"			{ return('%'); }
-"&"			{ return('&'); }
-"|"			{ return('|'); }
-"^"			{ return('^'); }
-"<<"			{ return(token::LSHIFT); }
-">>"			{ return(token::RSHIFT); }
-"&&"			{ return(token::LOGICAL_AND); }
-"||"			{ return(token::LOGICAL_OR);  }
-"!"			{ return('!'); }
-"~"			{ return('~'); }
-"<="			{ return(token::LEQ); }
-">="			{ return(token::GEQ); }
-"=="			{ return(token::EQUALITY); }
-"!="			{ return(token::NEQ); }
-"?"			{ return('?'); }
-"@"			{ return('@'); }
+"fmq_sync"          { yylval->type = new FmqType("::android::hardware", "MQDescriptorSync", *scope); return token::TEMPLATED; }
+"fmq_unsync"        { yylval->type = new FmqType("::android::hardware", "MQDescriptorUnsync", *scope); return token::TEMPLATED; }
 
-{COMPONENT}                     { yylval->str = strdup(yytext); return token::IDENTIFIER; }
-{FQNAME}                        { yylval->str = strdup(yytext); return token::FQNAME; }
+"("                 { return('('); }
+")"                 { return(')'); }
+"<"                 { return('<'); }
+">"                 { return('>'); }
+"{"                 { return('{'); }
+"}"                 { return('}'); }
+"["                 { return('['); }
+"]"                 { return(']'); }
+":"                 { return(':'); }
+";"                 { return(';'); }
+","                 { return(','); }
+"."                 { return('.'); }
+"="                 { return('='); }
+"+"                 { return('+'); }
+"-"                 { return('-'); }
+"*"                 { return('*'); }
+"/"                 { return('/'); }
+"%"                 { return('%'); }
+"&"                 { return('&'); }
+"|"                 { return('|'); }
+"^"                 { return('^'); }
+"<<"                { return(token::LSHIFT); }
+">>"                { return(token::RSHIFT); }
+"&&"                { return(token::LOGICAL_AND); }
+"||"                { return(token::LOGICAL_OR);  }
+"!"                 { return('!'); }
+"~"                 { return('~'); }
+"<="                { return(token::LEQ); }
+">="                { return(token::GEQ); }
+"=="                { return(token::EQUALITY); }
+"!="                { return(token::NEQ); }
+"?"                 { return('?'); }
+"@"                 { return('@'); }
 
-0[xX]{H}+{IS}?		{ yylval->str = strdup(yytext); return token::INTEGER; }
-0{D}+{IS}?		{ yylval->str = strdup(yytext); return token::INTEGER; }
-{D}+{IS}?		{ yylval->str = strdup(yytext); return token::INTEGER; }
-L?\"(\\.|[^\\"])*\"	{ yylval->str = strdup(yytext); return token::STRING_LITERAL; }
+{COMPONENT}         { yylval->str = strdup(yytext); return token::IDENTIFIER; }
+{FQNAME}            { yylval->str = strdup(yytext); return token::FQNAME; }
 
-{D}+{E}{FS}?		{ yylval->str = strdup(yytext); return token::FLOAT; }
-{D}+\.{E}?{FS}?		{ yylval->str = strdup(yytext); return token::FLOAT; }
-{D}*\.{D}+{E}?{FS}?	{ yylval->str = strdup(yytext); return token::FLOAT; }
+0[xX]{H}+{IS}?      { yylval->str = strdup(yytext); return token::INTEGER; }
+0{D}+{IS}?          { yylval->str = strdup(yytext); return token::INTEGER; }
+{D}+{IS}?           { yylval->str = strdup(yytext); return token::INTEGER; }
+L?\"(\\.|[^\\"])*\" { yylval->str = strdup(yytext); return token::STRING_LITERAL; }
 
-[\n]		{ yylloc->lines(); }
-.			{ /* ignore bad characters */ }
+{D}+{E}{FS}?        { yylval->str = strdup(yytext); return token::FLOAT; }
+{D}+\.{E}?{FS}?     { yylval->str = strdup(yytext); return token::FLOAT; }
+{D}*\.{D}+{E}?{FS}? { yylval->str = strdup(yytext); return token::FLOAT; }
+
+\n|\r\n             { yylloc->lines(); }
+[ \t\f\v]           { /* ignore all other whitespace */ }
+
+.                   { yylval->str = strdup(yytext); return token::UNKNOWN; }
 
 %%
 
 #pragma clang diagnostic pop
 
-status_t parseFile(AST *ast) {
-    FILE *file = fopen(ast->getFilename().c_str(), "rb");
+namespace android {
 
-    if (file == nullptr) {
-        return -errno;
-    }
-
+status_t parseFile(AST* ast, std::unique_ptr<FILE, std::function<void(FILE *)>> file) {
     yyscan_t scanner;
     yylex_init(&scanner);
 
-    yyset_in(file, scanner);
+    yyset_in(file.get(), scanner);
 
     Scope* scopeStack = ast->getRootScope();
     int res = yy::parser(scanner, ast, &scopeStack).parse();
 
     yylex_destroy(scanner);
 
-    fclose(file);
-    file = nullptr;
-
     if (res != 0 || ast->syntaxErrors() != 0) {
         return UNKNOWN_ERROR;
     }
 
     return OK;
 }
+
+}  // namespace android
diff --git a/hidl-gen_y.yy b/hidl-gen_y.yy
index d67638e..f445199 100644
--- a/hidl-gen_y.yy
+++ b/hidl-gen_y.yy
@@ -16,18 +16,20 @@
 
 %{
 
-#include "Annotation.h"
 #include "AST.h"
+#include "Annotation.h"
 #include "ArrayType.h"
 #include "CompoundType.h"
 #include "ConstantExpression.h"
+#include "DocComment.h"
 #include "EnumType.h"
 #include "Interface.h"
 #include "Location.h"
 #include "Method.h"
-#include "Scope.h"
-#include "VectorType.h"
 #include "RefType.h"
+#include "Scope.h"
+#include "TypeDef.h"
+#include "VectorType.h"
 
 #include "hidl-gen_y.h"
 
@@ -38,7 +40,7 @@
 
 using namespace android;
 
-extern int yylex(yy::parser::semantic_type*, yy::parser::location_type*, void*);
+extern int yylex(yy::parser::semantic_type*, yy::parser::location_type*, void*, Scope** const);
 
 void enterScope(AST* /* ast */, Scope** scope, Scope* container) {
     CHECK(container->parent() == (*scope));
@@ -205,6 +207,7 @@
 %parse-param { android::AST* const ast }
 %parse-param { android::Scope** const scope }
 %lex-param { void* scanner }
+%lex-param { android::Scope** const scope }
 %pure-parser
 %glr-parser
 %skeleton "glr.cc"
@@ -212,23 +215,28 @@
 %expect-rr 0
 %error-verbose
 
-%token<str> ENUM "keyword `enum`"
-%token<str> EXTENDS "keyword `extends`"
+%debug
+
+%token<docComment> DOC_COMMENT "doc comment"
+
+%token<void> ENUM "keyword `enum`"
+%token<void> EXTENDS "keyword `extends`"
 %token<str> FQNAME "fully-qualified name"
-%token<str> GENERATES "keyword `generates`"
+%token<void> GENERATES "keyword `generates`"
 %token<str> IDENTIFIER "identifier"
-%token<str> IMPORT "keyword `import`"
+%token<void> IMPORT "keyword `import`"
 %token<str> INTEGER "integer value"
 %token<str> FLOAT "float value"
-%token<str> INTERFACE "keyword `interface`"
+%token<void> INTERFACE "keyword `interface`"
 %token<str> PACKAGE "keyword `package`"
 %token<type> TYPE "type"
-%token<str> STRUCT "keyword `struct`"
+%token<void> STRUCT "keyword `struct`"
 %token<str> STRING_LITERAL "string literal"
-%token<str> TYPEDEF "keyword `typedef`"
-%token<str> UNION "keyword `union`"
+%token<void> TYPEDEF "keyword `typedef`"
+%token<void> UNION "keyword `union`"
 %token<templatedType> TEMPLATED "templated type"
 %token<void> ONEWAY "keyword `oneway`"
+%token<str> UNKNOWN "unknown character"
 
 /* Operator precedence and associativity, as per
  * http://en.cppreference.com/w/cpp/language/operator_precedence */
@@ -254,28 +262,31 @@
 /* Precedence level 3, RTL; but we have to use %left here */
 %left UNARY_MINUS UNARY_PLUS '!' '~'
 
+%type<docComment> doc_comments
+
 %type<str> error_stmt error
 %type<str> package
 %type<fqName> fqname
-%type<type> fqtype
+%type<referenceToType> fqtype
 %type<str> valid_identifier valid_type_name
 
-%type<type> type enum_storage_type
-%type<type> array_type_base
+%type<referenceToType> type enum_storage_type type_or_inplace_compound_declaration
+%type<referenceToType> array_type_base
 %type<arrayType> array_type
-%type<type> opt_extends
-%type<type> type_declaration type_declaration_body interface_declaration typedef_declaration
+%type<referenceToType> opt_extends
+%type<type> type_declaration commentable_type_declaration type_declaration_body
+%type<type> interface_declaration typedef_declaration
 %type<type> named_struct_or_union_declaration named_enum_declaration
 %type<type> compound_declaration annotated_compound_declaration
 
-%type<field> field_declaration
+%type<field> field_declaration commentable_field_declaration
 %type<fields> field_declarations struct_or_union_body
 %type<constantExpression> const_expr
-%type<enumValue> enum_value
+%type<enumValue> enum_value commentable_enum_value
 %type<enumValues> enum_values enum_declaration_body
 %type<typedVars> typed_vars
 %type<typedVar> typed_var
-%type<method> method_declaration
+%type<method> method_declaration commentable_method_declaration
 %type<compoundStyle> struct_or_union_keyword
 %type<stringVec> annotation_string_values annotation_string_value
 %type<constExprVec> annotation_const_expr_values annotation_const_expr_value
@@ -288,17 +299,18 @@
 
 %union {
     const char *str;
-    android::Type *type;
+    android::Type* type;
+    android::Reference<android::Type>* referenceToType;
     android::ArrayType *arrayType;
     android::TemplatedType *templatedType;
     android::FQName *fqName;
     android::CompoundType *compoundType;
-    android::CompoundField *field;
-    std::vector<android::CompoundField *> *fields;
+    android::NamedReference<android::Type>* field;
+    std::vector<android::NamedReference<android::Type>*>* fields;
     android::EnumValue *enumValue;
     android::ConstantExpression *constantExpression;
     std::vector<android::EnumValue *> *enumValues;
-    android::TypedVar *typedVar;
+    android::NamedReference<android::Type>* typedVar;
     android::TypedVarVector *typedVars;
     android::Method *method;
     android::CompoundType::Style compoundStyle;
@@ -308,14 +320,29 @@
     android::AnnotationParamVector *annotationParams;
     android::Annotation *annotation;
     std::vector<android::Annotation *> *annotations;
+    android::DocComment* docComment;
 }
 
 %%
 
 program
-    : package
-      imports
-      type_declarations
+    // Don't care if license header is a doc comment or not
+    : DOC_COMMENT package imports type_declarations
+    | package imports type_declarations
+    ;
+
+doc_comments
+    : DOC_COMMENT { $$ = $1; }
+    | doc_comments DOC_COMMENT
+      {
+        $1->merge($2);
+        $$ = $1;
+      }
+    | doc_comments '}'
+      {
+        std::cerr << "ERROR: Doc comments must preceed what they describe at " << @1 << "\n";
+        YYERROR;
+      }
     ;
 
 valid_identifier
@@ -388,11 +415,11 @@
 annotation_param
     : IDENTIFIER '=' annotation_string_value
       {
-          $$ = new AnnotationParam($1, $3);
+          $$ = new StringAnnotationParam($1, $3);
       }
     | IDENTIFIER '=' annotation_const_expr_value
       {
-          $$ = new AnnotationParam($1, $3);
+          $$ = new ConstantExpressionAnnotationParam($1, $3);
       }
     ;
 
@@ -460,8 +487,8 @@
 fqname
     : FQNAME
       {
-          $$ = new FQName($1);
-          if(!$$->isValid()) {
+          $$ = new FQName();
+          if(!FQName::parse($1, $$)) {
               std::cerr << "ERROR: FQName '" << $1 << "' is not valid at "
                         << @1
                         << ".\n";
@@ -470,8 +497,8 @@
       }
     | valid_type_name
       {
-          $$ = new FQName($1);
-          if(!$$->isValid()) {
+          $$ = new FQName();
+          if(!FQName::parse($1, $$)) {
               std::cerr << "ERROR: FQName '" << $1 << "' is not valid at "
                         << @1
                         << ".\n";
@@ -483,16 +510,12 @@
 fqtype
     : fqname
       {
-          $$ = ast->lookupType(*($1), *scope);
-          if ($$ == NULL) {
-              std::cerr << "ERROR: Failed to lookup type '" << $1->string() << "' at "
-                        << @1
-                        << "\n";
-
-              YYERROR;
-          }
+          $$ = new Reference<Type>(*$1, convertYYLoc(@1));
       }
     | TYPE
+      {
+          $$ = new Reference<Type>($1, convertYYLoc(@1));
+      }
     ;
 
 package
@@ -543,25 +566,29 @@
     ;
 
 opt_extends
-    : /* empty */ { $$ = NULL; }
+    : /* empty */ { $$ = nullptr; }
     | EXTENDS fqtype { $$ = $2; }
+    ;
 
 interface_declarations
     : /* empty */
-    | interface_declarations type_declaration
+    | interface_declarations commentable_type_declaration
       {
+          CHECK((*scope)->isInterface());
+
           std::string errorMsg;
-          if ($2 != nullptr &&
-              $2->isNamedType() &&
-              !isValidInterfaceField(static_cast<NamedType *>($2)->localName().c_str(),
+          if ($2 != nullptr && $2->isNamedType() &&
+              !isValidInterfaceField(static_cast<NamedType*>($2)->localName().c_str(),
                     &errorMsg)) {
               std::cerr << "ERROR: " << errorMsg << " at "
                         << @2 << "\n";
               YYERROR;
           }
       }
-    | interface_declarations method_declaration
+    | interface_declarations commentable_method_declaration
       {
+          CHECK((*scope)->isInterface());
+
           std::string errorMsg;
           if ($2 != nullptr &&
               !isValidInterfaceField($2->name().c_str(), &errorMsg)) {
@@ -571,13 +598,7 @@
           }
 
           if ($2 != nullptr) {
-            if (!(*scope)->isInterface()) {
-                std::cerr << "ERROR: unknown error in interface declaration at "
-                    << @2 << "\n";
-                YYERROR;
-            }
-
-            Interface *iface = static_cast<Interface *>(*scope);
+            Interface *iface = static_cast<Interface*>(*scope);
             if (!iface->addMethod($2)) {
                 std::cerr << "ERROR: Unable to add method '" << $2->name()
                           << "' at " << @2 << "\n";
@@ -592,19 +613,29 @@
 type_declarations
     : /* empty */
     | error_stmt
-    | type_declarations type_declaration
+    | type_declarations commentable_type_declaration
+    ;
+
+commentable_type_declaration
+    : doc_comments type_declaration
+      {
+        $2->setDocComment($1);
+        $$ = $2;
+      }
+    | type_declaration { $$ = $1; }
     ;
 
 type_declaration
     : opt_annotations type_declaration_body
       {
-          if ($2 != nullptr) {
-              $2->setAnnotations($1);
+          if (!$2->isTypeDef()) {
+              CHECK($2->isScope());
+              static_cast<Scope*>($2)->setAnnotations($1);
           } else if (!$1->empty()) {
               // Since typedefs are always resolved to their target it makes
               // little sense to annotate them and have their annotations
               // impose semantics other than their target type.
-              std::cerr << "ERROR: typedefs cannot be annotated. at " << @2
+              std::cerr << "ERROR: typedefs cannot be annotated at " << @2
                         << "\n";
 
               YYERROR;
@@ -623,9 +654,18 @@
 interface_declaration
     : INTERFACE valid_type_name opt_extends
       {
-          Type *parent = $3;
+          Reference<Type>* superType = $3;
+          bool isIBase = ast->package().package() == gIBaseFqName.package();
 
-          if (ast->package().package() != gIBasePackageFqName.string()) {
+          if (isIBase) {
+              if (superType != nullptr) {
+                  std::cerr << "ERROR: IBase must not extend any interface at " << @3
+                        << "\n";
+
+                  YYERROR;
+              }
+              superType = new Reference<Type>();
+          } else {
               if (!ast->addImport(gIBaseFqName.string().c_str())) {
                   std::cerr << "ERROR: Unable to automatically import '"
                             << gIBaseFqName.string()
@@ -633,63 +673,41 @@
                             << "\n";
                   YYERROR;
               }
-              if (parent == nullptr) {
-                parent = ast->lookupType(gIBaseFqName, *scope);
+
+              if (superType == nullptr) {
+                  superType = new Reference<Type>(gIBaseFqName, convertYYLoc(@$));
               }
           }
 
-          if (parent != NULL && !parent->isInterface()) {
-              std::cerr << "ERROR: You can only extend interfaces. at " << @3
-                        << "\n";
-
-              YYERROR;
-          }
-
           if ($2[0] != 'I') {
               std::cerr << "ERROR: All interface names must start with an 'I' "
-                        << "prefix. at " << @2 << "\n";
+                        << "prefix at " << @2 << "\n";
 
               YYERROR;
           }
 
           if (*scope != ast->getRootScope()) {
               std::cerr << "ERROR: All interface must declared in "
-                        << "global scope. at " << @2 << "\n";
+                        << "global scope at " << @2 << "\n";
 
               YYERROR;
           }
 
           Interface* iface = new Interface(
-              $2, convertYYLoc(@2), *scope,
-              static_cast<Interface *>(parent));
-
-          // Register interface immediately so it can be referenced inside
-          // definition.
-          std::string errorMsg;
-          if (!ast->addScopedType(iface, &errorMsg, *scope)) {
-              std::cerr << "ERROR: " << errorMsg << " at " << @2 << "\n";
-              YYERROR;
-          }
+              $2, ast->makeFullName($2, *scope), convertYYLoc(@2),
+              *scope, *superType, ast->getFileHash());
 
           enterScope(ast, scope, iface);
       }
       '{' interface_declarations '}'
       {
-          if (!(*scope)->isInterface()) {
-              std::cerr << "ERROR: unknown error in interface declaration at "
-                  << @5 << "\n";
-              YYERROR;
-          }
+          CHECK((*scope)->isInterface());
 
           Interface *iface = static_cast<Interface *>(*scope);
-          if (!iface->addAllReservedMethods()) {
-              std::cerr << "ERROR: unknown error in adding reserved methods at "
-                  << @5 << "\n";
-              YYERROR;
-          }
+          CHECK(iface->addAllReservedMethods());
 
           leaveScope(ast, scope);
-
+          ast->addScopedType(iface, *scope);
           $$ = iface;
       }
     ;
@@ -697,18 +715,26 @@
 typedef_declaration
     : TYPEDEF type valid_type_name
       {
-          std::string errorMsg;
-          if (!ast->addTypeDef($3, $2, convertYYLoc(@3), &errorMsg, *scope)) {
-              std::cerr << "ERROR: " << errorMsg << " at " << @3 << "\n";
-              YYERROR;
-          }
-
-          $$ = nullptr;
+          // The reason we wrap the given type in a TypeDef is simply to suppress
+          // emitting any type definitions later on, since this is just an alias
+          // to a type defined elsewhere.
+          TypeDef* typeDef = new TypeDef(
+              $3, ast->makeFullName($3, *scope), convertYYLoc(@2), *scope, *$2);
+          ast->addScopedType(typeDef, *scope);
+          $$ = typeDef;
       }
     ;
 
 const_expr
-    : INTEGER                   { $$ = new ConstantExpression($1); }
+    : INTEGER                   {
+          $$ = LiteralConstantExpression::tryParse($1);
+
+          if ($$ == nullptr) {
+              std::cerr << "ERROR: Could not parse literal: "
+                        << $1 << " at " << @1 << ".\n";
+              YYERROR;
+          }
+      }
     | fqname
       {
           if(!$1->isValidValueName()) {
@@ -717,79 +743,89 @@
                         << @1 << ".\n";
               YYERROR;
           }
-          if($1->isIdentifier()) {
-              std::string identifier = $1->name();
-              LocalIdentifier *iden = (*scope)->lookupIdentifier(identifier);
-              if(!iden) {
-                  std::cerr << "ERROR: identifier " << $1->string()
-                            << " could not be found at " << @1 << ".\n";
-                  YYERROR;
-              }
-              if(!iden->isEnumValue()) {
-                  std::cerr << "ERROR: identifier " << $1->string()
-                            << " is not an enum value at " << @1 << ".\n";
-                  YYERROR;
-              }
-              $$ = new ConstantExpression(
-                      *(static_cast<EnumValue *>(iden)->constExpr()), $1->string());
-          } else {
-              std::string errorMsg;
-              EnumValue *v = ast->lookupEnumValue(*($1), &errorMsg, *scope);
-              if(v == nullptr) {
-                  std::cerr << "ERROR: " << errorMsg << " at " << @1 << ".\n";
-                  YYERROR;
-              }
-              $$ = new ConstantExpression(*(v->constExpr()), $1->string());
-          }
+
+          $$ = new ReferenceConstantExpression(
+              Reference<LocalIdentifier>(*$1, convertYYLoc(@1)), $1->string());
       }
     | const_expr '?' const_expr ':' const_expr
       {
-          $$ = new ConstantExpression($1, $3, $5);
+          $$ = new TernaryConstantExpression($1, $3, $5);
       }
-    | const_expr LOGICAL_OR const_expr  { $$ = new ConstantExpression($1, "||", $3); }
-    | const_expr LOGICAL_AND const_expr { $$ = new ConstantExpression($1, "&&", $3); }
-    | const_expr '|' const_expr { $$ = new ConstantExpression($1, "|" , $3); }
-    | const_expr '^' const_expr { $$ = new ConstantExpression($1, "^" , $3); }
-    | const_expr '&' const_expr { $$ = new ConstantExpression($1, "&" , $3); }
-    | const_expr EQUALITY const_expr { $$ = new ConstantExpression($1, "==", $3); }
-    | const_expr NEQ const_expr { $$ = new ConstantExpression($1, "!=", $3); }
-    | const_expr '<' const_expr { $$ = new ConstantExpression($1, "<" , $3); }
-    | const_expr '>' const_expr { $$ = new ConstantExpression($1, ">" , $3); }
-    | const_expr LEQ const_expr { $$ = new ConstantExpression($1, "<=", $3); }
-    | const_expr GEQ const_expr { $$ = new ConstantExpression($1, ">=", $3); }
-    | const_expr LSHIFT const_expr { $$ = new ConstantExpression($1, "<<", $3); }
-    | const_expr RSHIFT const_expr { $$ = new ConstantExpression($1, ">>", $3); }
-    | const_expr '+' const_expr { $$ = new ConstantExpression($1, "+" , $3); }
-    | const_expr '-' const_expr { $$ = new ConstantExpression($1, "-" , $3); }
-    | const_expr '*' const_expr { $$ = new ConstantExpression($1, "*" , $3); }
-    | const_expr '/' const_expr { $$ = new ConstantExpression($1, "/" , $3); }
-    | const_expr '%' const_expr { $$ = new ConstantExpression($1, "%" , $3); }
-    | '+' const_expr %prec UNARY_PLUS  { $$ = new ConstantExpression("+", $2); }
-    | '-' const_expr %prec UNARY_MINUS { $$ = new ConstantExpression("-", $2); }
-    | '!' const_expr { $$ = new ConstantExpression("!", $2); }
-    | '~' const_expr { $$ = new ConstantExpression("~", $2); }
+    | const_expr LOGICAL_OR const_expr  { $$ = new BinaryConstantExpression($1, "||", $3); }
+    | const_expr LOGICAL_AND const_expr { $$ = new BinaryConstantExpression($1, "&&", $3); }
+    | const_expr '|' const_expr { $$ = new BinaryConstantExpression($1, "|" , $3); }
+    | const_expr '^' const_expr { $$ = new BinaryConstantExpression($1, "^" , $3); }
+    | const_expr '&' const_expr { $$ = new BinaryConstantExpression($1, "&" , $3); }
+    | const_expr EQUALITY const_expr { $$ = new BinaryConstantExpression($1, "==", $3); }
+    | const_expr NEQ const_expr { $$ = new BinaryConstantExpression($1, "!=", $3); }
+    | const_expr '<' const_expr { $$ = new BinaryConstantExpression($1, "<" , $3); }
+    | const_expr '>' const_expr { $$ = new BinaryConstantExpression($1, ">" , $3); }
+    | const_expr LEQ const_expr { $$ = new BinaryConstantExpression($1, "<=", $3); }
+    | const_expr GEQ const_expr { $$ = new BinaryConstantExpression($1, ">=", $3); }
+    | const_expr LSHIFT const_expr { $$ = new BinaryConstantExpression($1, "<<", $3); }
+    | const_expr RSHIFT const_expr { $$ = new BinaryConstantExpression($1, ">>", $3); }
+    | const_expr '+' const_expr { $$ = new BinaryConstantExpression($1, "+" , $3); }
+    | const_expr '-' const_expr { $$ = new BinaryConstantExpression($1, "-" , $3); }
+    | const_expr '*' const_expr { $$ = new BinaryConstantExpression($1, "*" , $3); }
+    | const_expr '/' const_expr { $$ = new BinaryConstantExpression($1, "/" , $3); }
+    | const_expr '%' const_expr { $$ = new BinaryConstantExpression($1, "%" , $3); }
+    | '+' const_expr %prec UNARY_PLUS  { $$ = new UnaryConstantExpression("+", $2); }
+    | '-' const_expr %prec UNARY_MINUS { $$ = new UnaryConstantExpression("-", $2); }
+    | '!' const_expr { $$ = new UnaryConstantExpression("!", $2); }
+    | '~' const_expr { $$ = new UnaryConstantExpression("~", $2); }
     | '(' const_expr ')' { $$ = $2; }
     | '(' error ')'
       {
         ast->addSyntaxError();
         // to avoid segfaults
-        $$ = new ConstantExpression(ConstantExpression::Zero(ScalarType::KIND_INT32));
+        $$ = ConstantExpression::Zero(ScalarType::KIND_INT32).release();
       }
     ;
 
+commentable_method_declaration
+    : doc_comments method_declaration
+      {
+        if ($2 != nullptr) $2->setDocComment($1);
+        $$ = $2;
+      }
+    | method_declaration
+      {
+        $$ = $1;
+      }
+
 method_declaration
     : error_stmt { $$ = nullptr; }
     | opt_annotations valid_identifier '(' typed_vars ')' require_semicolon
       {
-          $$ = new Method($2, $4, new std::vector<TypedVar *>, false, $1);
+          $$ = new Method($2 /* name */,
+                          $4 /* args */,
+                          new std::vector<NamedReference<Type>*> /* results */,
+                          false /* oneway */,
+                          $1 /* annotations */,
+                          convertYYLoc(@$));
       }
     | opt_annotations ONEWAY valid_identifier '(' typed_vars ')' require_semicolon
       {
-          $$ = new Method($3, $5, new std::vector<TypedVar *>, true, $1);
+          $$ = new Method($3 /* name */,
+                          $5 /* args */,
+                          new std::vector<NamedReference<Type>*> /* results */,
+                          true /* oneway */,
+                          $1 /* annotations */,
+                          convertYYLoc(@$));
       }
     | opt_annotations valid_identifier '(' typed_vars ')' GENERATES '(' typed_vars ')' require_semicolon
       {
-          $$ = new Method($2, $4, $8, false, $1);
+          if ($8->empty()) {
+              std::cerr << "ERROR: generates clause used without result at " << @1 << "\n";
+              ast->addSyntaxError();
+          }
+
+          $$ = new Method($2 /* name */,
+                          $4 /* args */,
+                          $8 /* results */,
+                          false /* oneway */,
+                          $1 /* annotations */,
+                          convertYYLoc(@$));
       }
     ;
 
@@ -818,7 +854,22 @@
       }
     ;
 
-typed_var : type valid_identifier { $$ = new TypedVar($2, $1); }
+typed_var
+    : type valid_identifier
+      {
+          $$ = new NamedReference<Type>($2, *$1, convertYYLoc(@2));
+      }
+    | type
+      {
+          $$ = new NamedReference<Type>("", *$1, convertYYLoc(@1));
+
+          const std::string typeName = $$->isResolved()
+              ? $$->get()->typeName() : $$->getLookupFqName().string();
+
+          std::cerr << "ERROR: variable of type " << typeName
+              << " is missing a variable name at " << @1 << "\n";
+          ast->addSyntaxError();
+      }
     ;
 
 
@@ -830,31 +881,18 @@
 named_struct_or_union_declaration
     : struct_or_union_keyword valid_type_name
       {
-          CompoundType *container = new CompoundType($1, $2, convertYYLoc(@2), *scope);
+          CompoundType *container = new CompoundType(
+              $1, $2, ast->makeFullName($2, *scope), convertYYLoc(@2), *scope);
           enterScope(ast, scope, container);
       }
       struct_or_union_body
       {
-          if (!(*scope)->isCompoundType()) {
-              std::cerr << "ERROR: unknown error in struct or union declaration at "
-                  << @4 << "\n";
-              YYERROR;
-          }
+          CHECK((*scope)->isCompoundType());
           CompoundType *container = static_cast<CompoundType *>(*scope);
-
-          std::string errorMsg;
-          if (!container->setFields($4, &errorMsg)) {
-              std::cerr << "ERROR: " << errorMsg << " at " << @4 << "\n";
-              YYERROR;
-          }
+          container->setFields($4);
 
           leaveScope(ast, scope);
-
-          if (!ast->addScopedType(container, &errorMsg, *scope)) {
-              std::cerr << "ERROR: " << errorMsg << " at " << @2 << "\n";
-              YYERROR;
-          }
-
+          ast->addScopedType(container, *scope);
           $$ = container;
       }
     ;
@@ -864,51 +902,63 @@
     ;
 
 field_declarations
-    : /* empty */ { $$ = new std::vector<CompoundField *>; }
-    | field_declarations field_declaration
+    : /* empty */ { $$ = new std::vector<NamedReference<Type>*>; }
+    | field_declarations commentable_field_declaration
       {
           $$ = $1;
 
-          if ($2 != NULL) {
+          // Compound declaration or error
+          if ($2 != nullptr) {
               $$->push_back($2);
           }
       }
     ;
 
+commentable_field_declaration
+    : doc_comments field_declaration
+    {
+      if ($2 != nullptr) $2->setDocComment($1);
+      $$ = $2;
+    }
+    | field_declaration { $$ = $1; }
+
 field_declaration
     : error_stmt { $$ = nullptr; }
-    | type valid_identifier require_semicolon
+    | type_or_inplace_compound_declaration valid_identifier require_semicolon
       {
-        std::string errorMsg;
-        if ((*scope)->isCompoundType() &&
-            static_cast<CompoundType *>(*scope)->style() == CompoundType::STYLE_STRUCT &&
-            !isValidStructField($2, &errorMsg)) {
-            std::cerr << "ERROR: " << errorMsg << " at "
-                      << @2 << "\n";
-            YYERROR;
-        }
-        $$ = new CompoundField($2, $1);
+          CHECK((*scope)->isCompoundType());
+
+          std::string errorMsg;
+          if (static_cast<CompoundType *>(*scope)->style() == CompoundType::STYLE_STRUCT &&
+              !isValidStructField($2, &errorMsg)) {
+              std::cerr << "ERROR: " << errorMsg << " at "
+                        << @2 << "\n";
+              YYERROR;
+          }
+          $$ = new NamedReference<Type>($2, *$1, convertYYLoc(@2));
       }
     | annotated_compound_declaration ';'
       {
-        std::string errorMsg;
-        if ((*scope)->isCompoundType() &&
-            static_cast<CompoundType *>(*scope)->style() == CompoundType::STYLE_STRUCT &&
-            $1 != nullptr &&
-            $1->isNamedType() &&
-            !isValidStructField(static_cast<NamedType *>($1)->localName().c_str(), &errorMsg)) {
-            std::cerr << "ERROR: " << errorMsg << " at "
-                      << @2 << "\n";
-            YYERROR;
-        }
-        $$ = NULL;
+          CHECK((*scope)->isCompoundType());
+
+          std::string errorMsg;
+          if (static_cast<CompoundType *>(*scope)->style() == CompoundType::STYLE_STRUCT &&
+              $1 != nullptr && $1->isNamedType() &&
+              !isValidStructField(static_cast<NamedType*>($1)->localName().c_str(), &errorMsg)) {
+              std::cerr << "ERROR: " << errorMsg << " at "
+                        << @2 << "\n";
+              YYERROR;
+          }
+          // Returns fields only
+          $$ = nullptr;
       }
     ;
 
 annotated_compound_declaration
     : opt_annotations compound_declaration
       {
-          $2->setAnnotations($1);
+          CHECK($2->isScope());
+          static_cast<Scope*>($2)->setAnnotations($1);
           $$ = $2;
       }
     ;
@@ -919,19 +969,8 @@
     ;
 
 enum_storage_type
-    : ':' fqtype
-      {
-          $$ = $2;
-
-          if ($$ != NULL && !$$->isValidEnumStorageType()) {
-              std::cerr << "ERROR: Invalid enum storage type ("
-                        << $2->typeName()
-                        << ") specified. at "
-                        << @2 << "\n";
-
-              YYERROR;
-          }
-      }
+    : ':' fqtype { $$ = $2; }
+    | /* empty */ { $$ = nullptr; }
     ;
 
 opt_comma
@@ -942,25 +981,27 @@
 named_enum_declaration
     : ENUM valid_type_name enum_storage_type
       {
-          enterScope(ast, scope, new EnumType($2, convertYYLoc(@2), $3, *scope));
+          auto storageType = $3;
+
+          if (storageType == nullptr) {
+              std::cerr << "ERROR: Must explicitly specify enum storage type for "
+                        << $2 << " at " << @2 << "\n";
+              ast->addSyntaxError();
+              storageType = new Reference<Type>(
+                  new ScalarType(ScalarType::KIND_INT64, *scope), convertYYLoc(@2));
+          }
+
+          EnumType* enumType = new EnumType(
+              $2, ast->makeFullName($2, *scope), convertYYLoc(@2), *storageType, *scope);
+          enterScope(ast, scope, enumType);
       }
       enum_declaration_body
       {
-          if (!(*scope)->isEnum()) {
-              std::cerr << "ERROR: unknown error in enum declaration at "
-                  << @5 << "\n";
-              YYERROR;
-          }
+          CHECK((*scope)->isEnum());
+          EnumType* enumType = static_cast<EnumType*>(*scope);
 
-          EnumType *enumType = static_cast<EnumType *>(*scope);
           leaveScope(ast, scope);
-
-          std::string errorMsg;
-          if (!ast->addScopedType(enumType, &errorMsg, *scope)) {
-              std::cerr << "ERROR: " << errorMsg << " at " << @2 << "\n";
-              YYERROR;
-          }
-
+          ast->addScopedType(enumType, *scope);
           $$ = enumType;
       }
     ;
@@ -969,83 +1010,74 @@
     : '{' enum_values opt_comma '}' { $$ = $2; }
     ;
 
+commentable_enum_value
+    : doc_comments enum_value
+      {
+        $2->setDocComment($1);
+        $$ = $2;
+      }
+    | enum_value { $$ = $1; }
+    ;
+
 enum_value
-    : valid_identifier { $$ = new EnumValue($1); }
-    | valid_identifier '=' const_expr { $$ = new EnumValue($1, $3); }
+    : valid_identifier
+      {
+          $$ = new EnumValue($1 /* name */, nullptr /* value */, convertYYLoc(@$));
+      }
+    | valid_identifier '=' const_expr
+      {
+          $$ = new EnumValue($1 /* name */, $3 /* value */, convertYYLoc(@$));
+      }
     ;
 
 enum_values
     : /* empty */
       { /* do nothing */ }
-    | enum_value
+    | commentable_enum_value
       {
-          if (!(*scope)->isEnum()) {
-              std::cerr << "ERROR: unknown error in enum declaration at "
-                  << @1 << "\n";
-              YYERROR;
-          }
-
+          CHECK((*scope)->isEnum());
           static_cast<EnumType *>(*scope)->addValue($1);
       }
-    | enum_values ',' enum_value
+    | enum_values ',' commentable_enum_value
       {
-          if (!(*scope)->isEnum()) {
-              std::cerr << "ERROR: unknown error in enum declaration at "
-                  << @3 << "\n";
-              YYERROR;
-          }
-
+          CHECK((*scope)->isEnum());
           static_cast<EnumType *>(*scope)->addValue($3);
       }
+    | error ',' commentable_enum_value
+      {
+          ast->addSyntaxError();
+
+          CHECK((*scope)->isEnum());
+          static_cast<EnumType *>(*scope)->addValue($3);
+      }
+    | enum_values ',' error ',' commentable_enum_value
+      {
+          ast->addSyntaxError();
+
+          CHECK((*scope)->isEnum());
+          static_cast<EnumType *>(*scope)->addValue($5);
+      }
     ;
 
 array_type_base
     : fqtype { $$ = $1; }
     | TEMPLATED '<' type '>'
       {
-          if (!$1->isCompatibleElementType($3)) {
-              std::cerr << "ERROR: " << $1->typeName() << " of " << $3->typeName()
-                        << " is not supported. at " << @3 << "\n";
-
-              YYERROR;
-          }
-          $1->setElementType($3);
-          $$ = $1;
+          $1->setElementType(*$3);
+          $$ = new Reference<Type>($1, convertYYLoc(@1));
       }
     | TEMPLATED '<' TEMPLATED '<' type RSHIFT
       {
-          if (!$3->isCompatibleElementType($5)) {
-              std::cerr << "ERROR: " << $3->typeName() << " of " << $5->typeName()
-                        << " is not supported. at " << @3 << "\n";
-
-              YYERROR;
-          }
-          $3->setElementType($5);
-          if (!$1->isCompatibleElementType($3)) {
-              std::cerr << "ERROR: " << $1->typeName() << " of " << $3->typeName()
-                        << " is not supported. at " << @3 << "\n";
-
-              YYERROR;
-          }
-          $1->setElementType($3);
-          $$ = $1;
+          $3->setElementType(*$5);
+          $1->setElementType(Reference<Type>($3, convertYYLoc(@3)));
+          $$ = new Reference<Type>($1, convertYYLoc(@1));
       }
     ;
 
 array_type
     : array_type_base '[' const_expr ']'
       {
-          if ($1->isBinder()) {
-              std::cerr << "ERROR: Arrays of interface types are not supported."
-                        << " at " << @1 << "\n";
-
-              YYERROR;
-          }
-          if ($1->isArray()) {
-              $$ = new ArrayType(static_cast<ArrayType *>($1), $3);
-          } else {
-              $$ = new ArrayType($1, $3);
-          }
+          $$ = new ArrayType(*$1, $3, *scope);
       }
     | array_type '[' const_expr ']'
       {
@@ -1056,26 +1088,24 @@
 
 type
     : array_type_base { $$ = $1; }
-    | array_type { $$ = $1; }
-    | annotated_compound_declaration { $$ = $1; }
+    | array_type { $$ = new Reference<Type>($1, convertYYLoc(@1)); }
     | INTERFACE
       {
           // "interface" is a synonym of android.hidl.base@1.0::IBase
-          $$ = ast->lookupType(gIBaseFqName, *scope);
-          if ($$ == nullptr) {
-              std::cerr << "ERROR: Cannot find "
-                        << gIBaseFqName.string()
-                        << " at " << @1 << "\n";
-
-              YYERROR;
+          $$ = new Reference<Type>(gIBaseFqName, convertYYLoc(@1));
       }
-    }
+    ;
+
+type_or_inplace_compound_declaration
+    : type { $$ = $1; }
+    | annotated_compound_declaration
+      {
+          $$ = new Reference<Type>($1, convertYYLoc(@1));
+      }
     ;
 
 %%
 
-#include <android-base/logging.h>
-
 void yy::parser::error(
         const yy::parser::location_type &where,
         const std::string &errstr) {
diff --git a/include_hash/hidl-hash/Hash.h b/include_hash/hidl-hash/Hash.h
index 71a96e8..e443ef5 100644
--- a/include_hash/hidl-hash/Hash.h
+++ b/include_hash/hidl-hash/Hash.h
@@ -23,15 +23,18 @@
 namespace android {
 
 struct Hash {
+    static const std::vector<uint8_t> kEmptyHash;
+
     // path to .hal file
     static const Hash &getHash(const std::string &path);
+    static void clearHash(const std::string& path);
 
     // returns matching hashes of interfaceName in path
     // path is something like hardware/interfaces/current.txt
     // interfaceName is something like android.hardware.foo@1.0::IFoo
-    static std::vector<std::string> lookupHash(const std::string &path,
-                                               const std::string &interfaceName,
-                                               std::string *err);
+    static std::vector<std::string> lookupHash(const std::string& path,
+                                               const std::string& interfaceName, std::string* err,
+                                               bool* fileExists = nullptr);
 
     static std::string hexString(const std::vector<uint8_t> &hash);
     std::string hexString() const;
@@ -42,8 +45,10 @@
 private:
     Hash(const std::string &path);
 
+    static Hash& getMutableHash(const std::string& path);
+
     const std::string mPath;
-    const std::vector<uint8_t> mHash;
+    std::vector<uint8_t> mHash;
 };
 
 }  // namespace android
diff --git a/main.cpp b/main.cpp
index 1b43e1e..dabbdaa 100644
--- a/main.cpp
+++ b/main.cpp
@@ -18,58 +18,277 @@
 #include "Coordinator.h"
 #include "Scope.h"
 
-#include <hidl-hash/Hash.h>
-#include <hidl-util/Formatter.h>
-#include <hidl-util/FQName.h>
-#include <hidl-util/StringHelper.h>
 #include <android-base/logging.h>
-#include <set>
+#include <hidl-hash/Hash.h>
+#include <hidl-util/FQName.h>
+#include <hidl-util/Formatter.h>
+#include <hidl-util/StringHelper.h>
 #include <stdio.h>
-#include <string>
+#include <sys/stat.h>
 #include <unistd.h>
+#include <iostream>
+#include <set>
+#include <string>
 #include <vector>
 
 using namespace android;
 
-struct OutputHandler {
-    std::string mKey;
-    std::string mDescription;
-    enum OutputMode {
-        NEEDS_DIR,
-        NEEDS_FILE,
-        NEEDS_SRC, // for changes inside the source tree itself
-        NOT_NEEDED
-    } mOutputMode;
-
-    const std::string& name() { return mKey; }
-    const std::string& description() { return mDescription; }
-
-    using ValidationFunction = std::function<bool(const FQName &, const std::string &language)>;
-    using GenerationFunction = std::function<status_t(const FQName &fqName,
-                                                      const char *hidl_gen,
-                                                      Coordinator *coordinator,
-                                                      const std::string &outputDir)>;
-
-    ValidationFunction validate;
-    GenerationFunction generate;
+enum class OutputMode {
+    NEEDS_DIR,   // -o output option expects a directory
+    NEEDS_FILE,  // -o output option expects a file
+    NEEDS_SRC,   // for changes inside the source tree itself
+    NOT_NEEDED   // does not create files
 };
 
-static bool generateForTest = false;
+enum class GenerationGranularity {
+    PER_PACKAGE,  // Files generated for each package
+    PER_FILE,     // Files generated for each hal file
+    PER_TYPE,     // Files generated for each hal file + each type in HAL files
+};
 
-static status_t generateSourcesForFile(
-        const FQName &fqName,
-        const char *,
-        Coordinator *coordinator,
-        const std::string &outputDir,
-        const std::string &lang) {
+// Represents a file that is generated by an -L option for an FQName
+struct FileGenerator {
+    using ShouldGenerateFunction = std::function<bool(const FQName& fqName)>;
+    using FileNameForFQName = std::function<std::string(const FQName& fqName)>;
+    using GenerationFunction = std::function<status_t(Formatter& out, const FQName& fqName,
+                                                      const Coordinator* coordinator)>;
+
+    ShouldGenerateFunction mShouldGenerateForFqName;  // If generate function applies to this target
+    FileNameForFQName mFileNameForFqName;             // Target -> filename
+    GenerationFunction mGenerationFunction;           // Function to generate output for file
+
+    std::string getFileName(const FQName& fqName) const {
+        return mFileNameForFqName ? mFileNameForFqName(fqName) : "";
+    }
+
+    status_t getOutputFile(const FQName& fqName, const Coordinator* coordinator,
+                           Coordinator::Location location, std::string* file) const {
+        if (!mShouldGenerateForFqName(fqName)) {
+            return OK;
+        }
+
+        return coordinator->getFilepath(fqName, location, getFileName(fqName), file);
+    }
+
+    status_t appendOutputFiles(const FQName& fqName, const Coordinator* coordinator,
+                               Coordinator::Location location,
+                               std::vector<std::string>* outputFiles) const {
+        if (location == Coordinator::Location::STANDARD_OUT) {
+            return OK;
+        }
+
+        if (mShouldGenerateForFqName(fqName)) {
+            std::string fileName;
+            status_t err = getOutputFile(fqName, coordinator, location, &fileName);
+            if (err != OK) return err;
+
+            if (!fileName.empty()) {
+                outputFiles->push_back(fileName);
+            }
+        }
+        return OK;
+    }
+
+    status_t generate(const FQName& fqName, const Coordinator* coordinator,
+                      Coordinator::Location location) const {
+        CHECK(mShouldGenerateForFqName != nullptr);
+        CHECK(mGenerationFunction != nullptr);
+
+        if (!mShouldGenerateForFqName(fqName)) {
+            return OK;
+        }
+
+        Formatter out = coordinator->getFormatter(fqName, location, getFileName(fqName));
+        if (!out.isValid()) {
+            return UNKNOWN_ERROR;
+        }
+
+        return mGenerationFunction(out, fqName, coordinator);
+    }
+
+    // Helper methods for filling out this struct
+    static bool generateForTypes(const FQName& fqName) {
+        const auto names = fqName.names();
+        return names.size() > 0 && names[0] == "types";
+    }
+    static bool generateForInterfaces(const FQName& fqName) { return !generateForTypes(fqName); }
+    static bool alwaysGenerate(const FQName&) { return true; }
+};
+
+// Represents a -L option, takes a fqName and generates files
+struct OutputHandler {
+    using ValidationFunction = std::function<bool(
+        const FQName& fqName, const Coordinator* coordinator, const std::string& language)>;
+
+    std::string mKey;                 // -L in Android.bp
+    std::string mDescription;         // for display in help menu
+    OutputMode mOutputMode;           // how this option interacts with -o
+    Coordinator::Location mLocation;  // how to compute location relative to the output directory
+    GenerationGranularity mGenerationGranularity;   // what to run generate function on
+    ValidationFunction mValidate;                   // if a given fqName is allowed for this option
+    std::vector<FileGenerator> mGenerateFunctions;  // run for each target at this granularity
+
+    const std::string& name() const { return mKey; }
+    const std::string& description() const { return mDescription; }
+
+    status_t generate(const FQName& fqName, const Coordinator* coordinator) const;
+    status_t validate(const FQName& fqName, const Coordinator* coordinator,
+                      const std::string& language) const {
+        return mValidate(fqName, coordinator, language);
+    }
+
+    status_t writeDepFile(const FQName& fqName, const Coordinator* coordinator) const;
+
+   private:
+    status_t appendTargets(const FQName& fqName, const Coordinator* coordinator,
+                           std::vector<FQName>* targets) const;
+    status_t appendOutputFiles(const FQName& fqName, const Coordinator* coordinator,
+                               std::vector<std::string>* outputFiles) const;
+};
+
+// Helper method for GenerationGranularity::PER_TYPE
+// IFoo -> IFoo, types.hal (containing Bar, Baz) -> types.Bar, types.Baz
+static status_t appendPerTypeTargets(const FQName& fqName, const Coordinator* coordinator,
+                                     std::vector<FQName>* exportedPackageInterfaces) {
     CHECK(fqName.isFullyQualified());
+    if (fqName.name() != "types") {
+        exportedPackageInterfaces->push_back(fqName);
+        return OK;
+    }
 
-    AST *ast;
+    AST* typesAST = coordinator->parse(fqName);
+    if (typesAST == nullptr) {
+        fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
+        return UNKNOWN_ERROR;
+    }
+
+    std::vector<NamedType*> rootTypes = typesAST->getRootScope()->getSubTypes();
+    for (const NamedType* rootType : rootTypes) {
+        if (rootType->isTypeDef()) continue;
+
+        FQName rootTypeName(fqName.package(), fqName.version(), "types." + rootType->localName());
+        exportedPackageInterfaces->push_back(rootTypeName);
+    }
+    return OK;
+}
+
+status_t OutputHandler::appendTargets(const FQName& fqName, const Coordinator* coordinator,
+                                      std::vector<FQName>* targets) const {
+    switch (mGenerationGranularity) {
+        case GenerationGranularity::PER_PACKAGE: {
+            targets->push_back(fqName.getPackageAndVersion());
+        } break;
+        case GenerationGranularity::PER_FILE: {
+            if (fqName.isFullyQualified()) {
+                targets->push_back(fqName);
+                break;
+            }
+            status_t err = coordinator->appendPackageInterfacesToVector(fqName, targets);
+            if (err != OK) return err;
+        } break;
+        case GenerationGranularity::PER_TYPE: {
+            if (fqName.isFullyQualified()) {
+                status_t err = appendPerTypeTargets(fqName, coordinator, targets);
+                if (err != OK) return err;
+            }
+
+            std::vector<FQName> packageInterfaces;
+            status_t err = coordinator->appendPackageInterfacesToVector(fqName, &packageInterfaces);
+            if (err != OK) return err;
+            for (const FQName& packageInterface : packageInterfaces) {
+                err = appendPerTypeTargets(packageInterface, coordinator, targets);
+                if (err != OK) return err;
+            }
+        } break;
+        default:
+            CHECK(!"Should be here");
+    }
+
+    return OK;
+}
+
+status_t OutputHandler::generate(const FQName& fqName, const Coordinator* coordinator) const {
+    std::vector<FQName> targets;
+    status_t err = appendTargets(fqName, coordinator, &targets);
+    if (err != OK) return err;
+
+    for (const FQName& fqName : targets) {
+        for (const FileGenerator& file : mGenerateFunctions) {
+            status_t err = file.generate(fqName, coordinator, mLocation);
+            if (err != OK) return err;
+        }
+    }
+
+    return OK;
+}
+
+status_t OutputHandler::appendOutputFiles(const FQName& fqName, const Coordinator* coordinator,
+                                          std::vector<std::string>* outputFiles) const {
+    std::vector<FQName> targets;
+    status_t err = appendTargets(fqName, coordinator, &targets);
+    if (err != OK) return err;
+
+    for (const FQName& fqName : targets) {
+        for (const FileGenerator& file : mGenerateFunctions) {
+            err = file.appendOutputFiles(fqName, coordinator, mLocation, outputFiles);
+            if (err != OK) return err;
+        }
+    }
+
+    return OK;
+}
+
+status_t OutputHandler::writeDepFile(const FQName& fqName, const Coordinator* coordinator) const {
+    std::vector<std::string> outputFiles;
+    status_t err = appendOutputFiles(fqName, coordinator, &outputFiles);
+    if (err != OK) return err;
+
+    // No need for dep files
+    if (outputFiles.empty()) {
+        return OK;
+    }
+
+    // Depfiles in Android for genrules should be for the 'main file'. Because hidl-gen doesn't have
+    // a main file for most targets, we are just outputting a depfile for one single file only.
+    const std::string forFile = outputFiles[0];
+
+    return coordinator->writeDepFile(forFile);
+}
+
+// Use an AST function as a OutputHandler GenerationFunction
+static FileGenerator::GenerationFunction astGenerationFunction(void (AST::*generate)(Formatter&)
+                                                                   const = nullptr) {
+    return [generate](Formatter& out, const FQName& fqName,
+                      const Coordinator* coordinator) -> status_t {
+        AST* ast = coordinator->parse(fqName);
+        if (ast == nullptr) {
+            fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
+            return UNKNOWN_ERROR;
+        }
+
+        if (generate == nullptr) return OK;  // just parsing AST
+        (ast->*generate)(out);
+
+        return OK;
+    };
+}
+
+// Common pattern: single file for package or standard out
+static FileGenerator singleFileGenerator(
+    const std::string& fileName, const FileGenerator::GenerationFunction& generationFunction) {
+    return {
+        FileGenerator::alwaysGenerate, [fileName](const FQName&) { return fileName; },
+        generationFunction,
+    };
+}
+
+static status_t generateJavaForPackage(Formatter& out, const FQName& fqName,
+                                       const Coordinator* coordinator) {
+    AST* ast;
     std::string limitToType;
 
+    // Required for legacy -Lmakefile files
     if (fqName.name().find("types.") == 0) {
-        CHECK(lang == "java");  // Already verified in validate().
-
         limitToType = fqName.name().substr(strlen("types."));
 
         FQName typesName = fqName.getTypesForPackage();
@@ -77,265 +296,49 @@
     } else {
         ast = coordinator->parse(fqName);
     }
-
-    if (ast == NULL) {
-        fprintf(stderr,
-                "ERROR: Could not parse %s. Aborting.\n",
-                fqName.string().c_str());
-
+    if (ast == nullptr) {
+        fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
         return UNKNOWN_ERROR;
     }
+    ast->generateJava(out, limitToType);
+    return OK;
+};
 
-    if (lang == "check") {
-        return OK; // only parsing, not generating
-    }
-    if (lang == "c++") {
-        return ast->generateCpp(outputDir);
-    }
-    if (lang == "c++-headers") {
-        return ast->generateCppHeaders(outputDir);
-    }
-    if (lang == "c++-sources") {
-        return ast->generateCppSources(outputDir);
-    }
-    if (lang == "c++-impl") {
-        return ast->generateCppImpl(outputDir);
-    }
-    if (lang == "c++-impl-headers") {
-        return ast->generateStubImplHeader(outputDir);
-    }
-    if (lang == "c++-impl-sources") {
-        return ast->generateStubImplSource(outputDir);
-    }
-    if (lang == "java") {
-        return ast->generateJava(outputDir, limitToType);
-    }
-    if (lang == "vts") {
-        return ast->generateVts(outputDir);
-    }
-    // Unknown language.
-    return UNKNOWN_ERROR;
-}
-
-static status_t generateSourcesForPackage(
-        const FQName &packageFQName,
-        const char *hidl_gen,
-        Coordinator *coordinator,
-        const std::string &outputDir,
-        const std::string &lang) {
-    CHECK(packageFQName.isValid() &&
-        !packageFQName.isFullyQualified() &&
-        packageFQName.name().empty());
-
+static status_t dumpDefinedButUnreferencedTypeNames(const FQName& packageFQName,
+                                                    const Coordinator* coordinator) {
     std::vector<FQName> packageInterfaces;
+    status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces);
+    if (err != OK) return err;
 
-    status_t err =
-        coordinator->appendPackageInterfacesToVector(packageFQName,
-                                                     &packageInterfaces);
+    std::set<FQName> unreferencedDefinitions;
+    std::set<FQName> unreferencedImports;
+    err = coordinator->addUnreferencedTypes(packageInterfaces, &unreferencedDefinitions,
+                                            &unreferencedImports);
+    if (err != OK) return err;
 
-    if (err != OK) {
-        return err;
+    for (const auto& fqName : unreferencedDefinitions) {
+        std::cerr
+            << "VERBOSE: DEFINED-BUT-NOT-REFERENCED "
+            << fqName.string()
+            << std::endl;
     }
 
-    for (const auto &fqName : packageInterfaces) {
-        err = generateSourcesForFile(
-                fqName, hidl_gen, coordinator, outputDir, lang);
-        if (err != OK) {
-            return err;
-        }
+    for (const auto& fqName : unreferencedImports) {
+        std::cerr
+            << "VERBOSE: IMPORTED-BUT-NOT-REFERENCED "
+            << fqName.string()
+            << std::endl;
     }
 
     return OK;
 }
 
-OutputHandler::GenerationFunction generationFunctionForFileOrPackage(const std::string &language) {
-    return [language](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,
-                                                  language);
-        } else {
-                    return generateSourcesForPackage(fqName,
-                                                     hidl_gen,
-                                                     coordinator,
-                                                     outputDir,
-                                                     language);
-        }
-    };
-}
-
 static std::string makeLibraryName(const FQName &packageFQName) {
     return packageFQName.string();
 }
 
-static std::string makeJavaLibraryName(const FQName &packageFQName) {
-    std::string out;
-    out = packageFQName.package();
-    out += "-V";
-    out += packageFQName.version();
-    return out;
-}
-
-static void generatePackagePathsSection(
-        Formatter &out,
-        Coordinator *coordinator,
-        const FQName &packageFQName,
-        const std::set<FQName> &importedPackages,
-        bool forMakefiles = false) {
-    std::set<std::string> options{};
-    for (const auto &interface : importedPackages) {
-        options.insert(coordinator->getPackageRootOption(interface));
-    }
-    options.insert(coordinator->getPackageRootOption(packageFQName));
-    options.insert(coordinator->getPackageRootOption(gIBaseFqName));
-    for (const auto &option : options) {
-        out << "-r"
-            << option
-            << " ";
-        if (forMakefiles) {
-            out << "\\\n";
-        }
-    }
-}
-
-static void generateMakefileSectionForType(
-        Formatter &out,
-        Coordinator *coordinator,
-        const FQName &packageFQName,
-        const FQName &fqName,
-        const std::set<FQName> &importedPackages,
-        const char *typeName) {
-    out << "\n"
-        << "\n#"
-        << "\n# Build " << fqName.name() << ".hal";
-
-    if (typeName != nullptr) {
-        out << " (" << typeName << ")";
-    }
-
-    out << "\n#"
-        << "\nGEN := $(intermediates)/"
-        << coordinator->convertPackageRootToPath(packageFQName)
-        << coordinator->getPackagePath(packageFQName, true /* relative */,
-                true /* sanitized */);
-    if (typeName == nullptr) {
-        out << fqName.name() << ".java";
-    } else {
-        out << typeName << ".java";
-    }
-
-    out << "\n$(GEN): $(HIDL)";
-    out << "\n$(GEN): PRIVATE_HIDL := $(HIDL)";
-    out << "\n$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/"
-        << fqName.name() << ".hal";
-
-    {
-        AST *ast = coordinator->parse(fqName);
-        CHECK(ast != nullptr);
-        const std::set<FQName>& refs = ast->getImportedNames();
-        for (auto depFQName : refs) {
-            // If the package of depFQName is the same as this fqName's package,
-            // then add it explicitly as a .hal dependency within the same
-            // package.
-            if (fqName.package() == depFQName.package() &&
-                fqName.version() == depFQName.version()) {
-                    // PRIVATE_DEPS is not actually being used in the
-                    // auto-generated file, but is necessary if the build rule
-                    // ever needs to use the dependency information, since the
-                    // built-in Make variables are not supported in the Android
-                    // build system.
-                    out << "\n$(GEN): PRIVATE_DEPS += $(LOCAL_PATH)/"
-                        << depFQName.name() << ".hal";
-                    // This is the actual dependency.
-                    out << "\n$(GEN): $(LOCAL_PATH)/"
-                        << depFQName.name() << ".hal";
-            }
-        }
-    }
-
-    out << "\n$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)"
-        << "\n$(GEN): PRIVATE_CUSTOM_TOOL = \\";
-    out.indent();
-    out.indent();
-    out << "\n$(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \\"
-        << "\n-Ljava \\\n";
-
-    generatePackagePathsSection(out, coordinator, packageFQName, importedPackages, true /* forJava */);
-
-    out << packageFQName.string()
-        << "::"
-        << fqName.name();
-
-    if (typeName != nullptr) {
-        out << "." << typeName;
-    }
-
-    out << "\n";
-
-    out.unindent();
-    out.unindent();
-
-    out << "\n$(GEN): $(LOCAL_PATH)/" << fqName.name() << ".hal";
-    out << "\n\t$(transform-generated-source)";
-    out << "\nLOCAL_GENERATED_SOURCES += $(GEN)";
-}
-
-static void generateMakefileSection(
-        Formatter &out,
-        Coordinator *coordinator,
-        const FQName &packageFQName,
-        const std::vector<FQName> &packageInterfaces,
-        const std::set<FQName> &importedPackages,
-        AST *typesAST) {
-    for (const auto &fqName : packageInterfaces) {
-        if (fqName.name() == "types") {
-            CHECK(typesAST != nullptr);
-
-            Scope* rootScope = typesAST->getRootScope();
-
-            std::vector<NamedType *> subTypes = rootScope->getSubTypes();
-            std::sort(
-                    subTypes.begin(),
-                    subTypes.end(),
-                    [](const NamedType *a, const NamedType *b) -> bool {
-                        return a->fqName() < b->fqName();
-                    });
-
-            for (const auto &type : subTypes) {
-                if (type->isTypeDef()) {
-                    continue;
-                }
-
-                generateMakefileSectionForType(
-                        out,
-                        coordinator,
-                        packageFQName,
-                        fqName,
-                        importedPackages,
-                        type->localName().c_str());
-            }
-
-            continue;
-        }
-
-        generateMakefileSectionForType(
-                out,
-                coordinator,
-                packageFQName,
-                fqName,
-                importedPackages,
-                nullptr /* typeName */);
-    }
-}
-
-static status_t isPackageJavaCompatible(
-        const FQName &packageFQName,
-        Coordinator *coordinator,
-        bool *compatible) {
+static status_t isPackageJavaCompatible(const FQName& packageFQName, const Coordinator* coordinator,
+                                        bool* compatible) {
     std::vector<FQName> todo;
     status_t err =
         coordinator->appendPackageInterfacesToVector(packageFQName, &todo);
@@ -395,6 +398,10 @@
 
 static bool packageNeedsJavaCode(
         const std::vector<FQName> &packageInterfaces, AST *typesAST) {
+    if (packageInterfaces.size() == 0) {
+        return false;
+    }
+
     // If there is more than just a types.hal file to this package we'll
     // definitely need to generate Java code.
     if (packageInterfaces.size() > 1
@@ -419,219 +426,8 @@
     return false;
 }
 
-static void generateMakefileSectionForJavaConstants(
-        Formatter &out,
-        Coordinator *coordinator,
-        const FQName &packageFQName,
-        const std::vector<FQName> &packageInterfaces,
-        const std::set<FQName> &importedPackages) {
-    out << "\n#"
-        << "\nGEN := $(intermediates)/"
-        << coordinator->convertPackageRootToPath(packageFQName)
-        << coordinator->getPackagePath(packageFQName, true /* relative */, true /* sanitized */)
-        << "Constants.java";
-
-    out << "\n$(GEN): $(HIDL)\n";
-    for (const auto &iface : packageInterfaces) {
-        out << "$(GEN): $(LOCAL_PATH)/" << iface.name() << ".hal\n";
-    }
-    out << "\n$(GEN): PRIVATE_HIDL := $(HIDL)";
-    out << "\n$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)"
-        << "\n$(GEN): PRIVATE_CUSTOM_TOOL = \\";
-    out.indent();
-    out.indent();
-    out << "\n$(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \\"
-        << "\n-Ljava-constants \\\n";
-
-    generatePackagePathsSection(out, coordinator, packageFQName, importedPackages, true /* forJava */);
-
-    out << packageFQName.string();
-    out << "\n";
-
-    out.unindent();
-    out.unindent();
-
-    out << "\n$(GEN):";
-    out << "\n\t$(transform-generated-source)";
-    out << "\nLOCAL_GENERATED_SOURCES += $(GEN)";
-}
-
-static status_t generateMakefileForPackage(
-        const FQName &packageFQName,
-        const char *hidl_gen,
-        Coordinator *coordinator,
-        const std::string &outputPath) {
-
-    CHECK(packageFQName.isValid() &&
-          !packageFQName.isFullyQualified() &&
-          packageFQName.name().empty());
-
-    std::vector<FQName> packageInterfaces;
-
-    status_t err =
-        coordinator->appendPackageInterfacesToVector(packageFQName,
-                                                     &packageInterfaces);
-
-    if (err != OK) {
-        return err;
-    }
-
-    std::set<FQName> importedPackages;
-    AST *typesAST = nullptr;
-    std::vector<const Type *> exportedTypes;
-
-    for (const auto &fqName : packageInterfaces) {
-        AST *ast = coordinator->parse(fqName);
-
-        if (ast == NULL) {
-            fprintf(stderr,
-                    "ERROR: Could not parse %s. Aborting.\n",
-                    fqName.string().c_str());
-
-            return UNKNOWN_ERROR;
-        }
-
-        if (fqName.name() == "types") {
-            typesAST = ast;
-        }
-
-        ast->getImportedPackagesHierarchy(&importedPackages);
-        ast->appendToExportedTypesVector(&exportedTypes);
-    }
-
-    bool packageIsJavaCompatible;
-    err = isPackageJavaCompatible(
-            packageFQName, coordinator, &packageIsJavaCompatible);
-
-    if (err != OK) {
-        return err;
-    }
-
-    bool haveJavaConstants = !exportedTypes.empty();
-
-    if (!packageIsJavaCompatible && !haveJavaConstants) {
-        // TODO(b/33420795)
-        fprintf(stderr,
-                "WARNING: %s is not java compatible. No java makefile created.\n",
-                packageFQName.string().c_str());
-        return OK;
-    }
-
-    if (!packageNeedsJavaCode(packageInterfaces, typesAST)) {
-        return OK;
-    }
-
-    std::string path = outputPath;
-    path.append(coordinator->getPackagePath(packageFQName, false /* relative */));
-    path.append("Android.mk");
-
-    CHECK(Coordinator::MakeParentHierarchy(path));
-    FILE *file = fopen(path.c_str(), "w");
-
-    if (file == NULL) {
-        return -errno;
-    }
-
-    const std::string libraryName = makeJavaLibraryName(packageFQName);
-
-    Formatter out(file);
-
-    out << "# This file is autogenerated by hidl-gen. Do not edit manually.\n\n";
-    out << "LOCAL_PATH := $(call my-dir)\n";
-
-    enum LibraryStyle {
-        LIBRARY_STYLE_REGULAR,
-        LIBRARY_STYLE_STATIC,
-        LIBRARY_STYLE_END,
-    };
-
-    for (int style = LIBRARY_STYLE_REGULAR;
-            (packageIsJavaCompatible && style != LIBRARY_STYLE_END);
-            ++style) {
-        const std::string staticSuffix =
-            (style == LIBRARY_STYLE_STATIC) ? "-static" : "";
-
-        out << "\n"
-            << "########################################"
-            << "########################################\n\n";
-
-        out << "include $(CLEAR_VARS)\n"
-            << "LOCAL_MODULE := "
-            << libraryName
-            << "-java"
-            << staticSuffix
-            << "\nLOCAL_MODULE_CLASS := JAVA_LIBRARIES\n\n"
-            << "intermediates := $(call local-generated-sources-dir, COMMON)"
-            << "\n\n"
-            << "HIDL := $(HOST_OUT_EXECUTABLES)/"
-            << hidl_gen
-            << "$(HOST_EXECUTABLE_SUFFIX)";
-
-        if (!importedPackages.empty()) {
-            out << "\n"
-                << "\nLOCAL_"
-                << ((style == LIBRARY_STYLE_STATIC) ? "STATIC_" : "")
-                << "JAVA_LIBRARIES := \\";
-
-            out.indent();
-            for (const auto &importedPackage : importedPackages) {
-                out << "\n"
-                    << makeJavaLibraryName(importedPackage)
-                    << "-java"
-                    << staticSuffix
-                    << " \\";
-            }
-            out << "\n";
-            out.unindent();
-        }
-
-        generateMakefileSection(
-                out,
-                coordinator,
-                packageFQName,
-                packageInterfaces,
-                importedPackages,
-                typesAST);
-
-        out << "\ninclude $(BUILD_"
-            << ((style == LIBRARY_STYLE_STATIC) ? "STATIC_" : "")
-            << "JAVA_LIBRARY)\n\n";
-    }
-
-    if (haveJavaConstants) {
-        out << "\n"
-            << "########################################"
-            << "########################################\n\n";
-
-        out << "include $(CLEAR_VARS)\n"
-            << "LOCAL_MODULE := "
-            << libraryName
-            << "-java-constants"
-            << "\nLOCAL_MODULE_CLASS := JAVA_LIBRARIES\n\n"
-            << "intermediates := $(call local-generated-sources-dir, COMMON)"
-            << "\n\n"
-            << "HIDL := $(HOST_OUT_EXECUTABLES)/"
-            << hidl_gen
-            << "$(HOST_EXECUTABLE_SUFFIX)";
-
-        generateMakefileSectionForJavaConstants(
-                out, coordinator, packageFQName, packageInterfaces, importedPackages);
-
-        out << "\n# Avoid dependency cycle of framework.jar -> this-library "
-            << "-> framework.jar\n"
-            << "LOCAL_NO_STANDARD_LIBRARIES := true\n"
-            << "LOCAL_JAVA_LIBRARIES := core-oj\n\n"
-            << "include $(BUILD_STATIC_JAVA_LIBRARY)\n\n";
-    }
-
-    out << "\n\n"
-        << "include $(call all-makefiles-under,$(LOCAL_PATH))\n";
-
-    return OK;
-}
-
-bool validateIsPackage(
-        const FQName &fqName, const std::string & /* language */) {
+bool validateIsPackage(const FQName& fqName, const Coordinator*,
+                       const std::string& /* language */) {
     if (fqName.package().empty()) {
         fprintf(stderr, "ERROR: Expecting package name\n");
         return false;
@@ -652,17 +448,19 @@
 }
 
 bool isHidlTransportPackage(const FQName& fqName) {
-    return fqName.package() == gIBasePackageFqName.string() ||
-           fqName.package() == gIManagerPackageFqName.string();
+    return fqName.package() == gIBaseFqName.package() ||
+           fqName.package() == gIManagerFqName.package();
 }
 
 bool isSystemProcessSupportedPackage(const FQName& fqName) {
     // Technically, so is hidl IBase + IServiceManager, but
     // these are part of libhidltransport.
-    return fqName.string() == "android.hardware.graphics.allocator@2.0" ||
-           fqName.string() == "android.hardware.graphics.common@1.0" ||
+    return fqName.string() == "android.hardware.graphics.common@1.0" ||
+           fqName.string() == "android.hardware.graphics.common@1.1" ||
            fqName.string() == "android.hardware.graphics.mapper@2.0" ||
+           fqName.string() == "android.hardware.graphics.mapper@2.1" ||
            fqName.string() == "android.hardware.renderscript@1.0" ||
+           fqName.string() == "android.hidl.memory.token@1.0" ||
            fqName.string() == "android.hidl.memory@1.0";
 }
 
@@ -673,155 +471,89 @@
            package.inPackage("android.hardware");
 }
 
-static void generateAndroidBpGenSection(
-        Formatter &out,
-        const FQName &packageFQName,
-        const char *hidl_gen,
-        Coordinator *coordinator,
-        const std::string &halFilegroupName,
-        const std::string &genName,
-        const char *language,
-        const std::vector<FQName> &packageInterfaces,
-        const std::set<FQName> &importedPackages,
-        const std::function<void(Formatter&, const FQName)> outputFn) {
+// TODO(b/69862859): remove special case
+status_t isTestPackage(const FQName& fqName, const Coordinator* coordinator, bool* isTestPackage) {
+    const auto fileExists = [](const std::string& file) {
+        struct stat buf;
+        return stat(file.c_str(), &buf) == 0;
+    };
 
-    out << "genrule {\n";
-    out.indent();
-    out << "name: \"" << genName << "\",\n"
-        << "tools: [\"" << hidl_gen << "\"],\n";
+    std::string path;
+    status_t err = coordinator->getFilepath(fqName, Coordinator::Location::PACKAGE_ROOT,
+                                            ".hidl_for_test", &path);
+    if (err != OK) return err;
 
-    out << "cmd: \"$(location " << hidl_gen << ") -o $(genDir)"
-        << " -L" << language << " ";
+    const bool exists = fileExists(path);
 
-    generatePackagePathsSection(out, coordinator, packageFQName, importedPackages);
-
-    out << packageFQName.string() << "\",\n";
-
-    out << "srcs: [\n";
-    out.indent();
-    out << "\":" << halFilegroupName << "\",\n";
-    out.unindent();
-    out << "],\n";
-
-    out << "out: [\n";
-    out.indent();
-    for (const auto &fqName : packageInterfaces) {
-        outputFn(out, fqName);
+    if (exists) {
+        coordinator->onFileAccess(path, "r");
     }
-    out.unindent();
-    out << "],\n";
 
-    out.unindent();
-    out << "}\n\n";
+    *isTestPackage = exists;
+    return OK;
 }
 
-static void generateAndroidBpDependencyList(
-        Formatter &out,
-        const std::set<FQName> &importedPackagesHierarchy,
-        bool generateVendor) {
-    for (const auto &importedPackage : importedPackagesHierarchy) {
-        if (isHidlTransportPackage(importedPackage)) {
+static status_t generateAdapterMainSource(Formatter& out, const FQName& packageFQName,
+                                          const Coordinator* coordinator) {
+    std::vector<FQName> packageInterfaces;
+    status_t err =
+        coordinator->appendPackageInterfacesToVector(packageFQName,
+                                                     &packageInterfaces);
+    if (err != OK) {
+        return err;
+    }
+
+    out << "#include <hidladapter/HidlBinderAdapter.h>\n";
+
+    for (auto &interface : packageInterfaces) {
+        if (interface.name() == "types") {
             continue;
         }
-
-        out << "\"" << makeLibraryName(importedPackage);
-        if (generateVendor && !isSystemPackage(importedPackage)) {
-            out << "_vendor";
-        }
-        out << "\",\n";
+        AST::generateCppPackageInclude(out, interface, interface.getInterfaceAdapterName());
     }
+
+    out << "int main(int argc, char** argv) ";
+    out.block([&] {
+        out << "return ::android::hardware::adapterMain<\n";
+        out.indent();
+        for (auto &interface : packageInterfaces) {
+            if (interface.name() == "types") {
+                continue;
+            }
+            out << interface.getInterfaceAdapterFqName().cppName();
+
+            if (&interface != &packageInterfaces.back()) {
+                out << ",\n";
+            }
+        }
+        out << ">(\"" << packageFQName.string() << "\", argc, argv);\n";
+        out.unindent();
+    }).endl();
+    return OK;
 }
 
-static void generateAndroidBpLibSection(
-        Formatter &out,
-        bool generateVendor,
-        const FQName &packageFQName,
-        const std::string &libraryName,
-        const std::string &genSourceName,
-        const std::string &genHeaderName,
-        const std::set<FQName> &importedPackagesHierarchy) {
-
-    // C++ library definition
-    out << "cc_library {\n";
-    out.indent();
-    out << "name: \"" << libraryName << (generateVendor ? "_vendor" : "") << "\",\n"
-        << "defaults: [\"hidl-module-defaults\"],\n"
-        << "generated_sources: [\"" << genSourceName << "\"],\n"
-        << "generated_headers: [\"" << genHeaderName << "\"],\n"
-        << "export_generated_headers: [\"" << genHeaderName << "\"],\n";
-
-    if (generateVendor) {
-        out << "vendor: true,\n";
-    } else {
-        out << "vendor_available: true,\n";
-        if (!generateForTest) {
-            out << "vndk: ";
-            out.block([&]() {
-                out << "enabled: true,\n";
-                if (isSystemProcessSupportedPackage(packageFQName)) {
-                    out << "support_system_process: true,\n";
-                }
-            }) << ",\n";
-        }
-    }
-    out << "shared_libs: [\n";
-
-    out.indent();
-    out << "\"libhidlbase\",\n"
-        << "\"libhidltransport\",\n"
-        << "\"libhwbinder\",\n"
-        << "\"liblog\",\n"
-        << "\"libutils\",\n"
-        << "\"libcutils\",\n";
-    generateAndroidBpDependencyList(out, importedPackagesHierarchy, generateVendor);
-    out.unindent();
-
-    out << "],\n";
-
-    out << "export_shared_lib_headers: [\n";
-    out.indent();
-    out << "\"libhidlbase\",\n"
-        << "\"libhidltransport\",\n"
-        << "\"libhwbinder\",\n"
-        << "\"libutils\",\n";
-    generateAndroidBpDependencyList(out, importedPackagesHierarchy, generateVendor);
-    out.unindent();
-    out << "],\n";
-    out.unindent();
-
-    out << "}\n";
-}
-
-static status_t generateAndroidBpForPackage(
-        const FQName &packageFQName,
-        const char *hidl_gen,
-        Coordinator *coordinator,
-        const std::string &outputPath) {
-
-    CHECK(packageFQName.isValid() &&
-          !packageFQName.isFullyQualified() &&
+static status_t generateAndroidBpForPackage(Formatter& out, const FQName& packageFQName,
+                                            const Coordinator* coordinator) {
+    CHECK(packageFQName.isValid() && !packageFQName.isFullyQualified() &&
           packageFQName.name().empty());
 
     std::vector<FQName> packageInterfaces;
 
-    status_t err =
-        coordinator->appendPackageInterfacesToVector(packageFQName,
-                                                     &packageInterfaces);
+    status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces);
 
     if (err != OK) {
         return err;
     }
 
     std::set<FQName> importedPackagesHierarchy;
-    AST *typesAST = nullptr;
+    std::vector<const Type *> exportedTypes;
+    AST* typesAST = nullptr;
 
-    for (const auto &fqName : packageInterfaces) {
-        AST *ast = coordinator->parse(fqName);
+    for (const auto& fqName : packageInterfaces) {
+        AST* ast = coordinator->parse(fqName);
 
         if (ast == NULL) {
-            fprintf(stderr,
-                    "ERROR: Could not parse %s. Aborting.\n",
-                    fqName.string().c_str());
+            fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
 
             return UNKNOWN_ERROR;
         }
@@ -831,134 +563,93 @@
         }
 
         ast->getImportedPackagesHierarchy(&importedPackagesHierarchy);
+        ast->appendToExportedTypesVector(&exportedTypes);
     }
 
-    std::string path = outputPath;
-    path.append(coordinator->getPackagePath(packageFQName, false /* relative */));
-    path.append("Android.bp");
+    bool needsJavaCode = packageNeedsJavaCode(packageInterfaces, typesAST);
 
-    CHECK(Coordinator::MakeParentHierarchy(path));
-    FILE *file = fopen(path.c_str(), "w");
+    bool genJavaConstants = needsJavaCode && !exportedTypes.empty();
 
-    if (file == NULL) {
-        return -errno;
-    }
+    bool isJavaCompatible;
+    err = isPackageJavaCompatible(packageFQName, coordinator, &isJavaCompatible);
+    if (err != OK) return err;
+    bool genJavaLibrary = needsJavaCode && isJavaCompatible;
 
-    const std::string libraryName = makeLibraryName(packageFQName);
-    const std::string halFilegroupName = libraryName + "_hal";
-    const std::string genSourceName = libraryName + "_genc++";
-    const std::string genHeaderName = libraryName + "_genc++_headers";
-    const std::string pathPrefix =
-        coordinator->convertPackageRootToPath(packageFQName) +
-        coordinator->getPackagePath(packageFQName, true /* relative */);
+    bool generateForTest;
+    err = isTestPackage(packageFQName, coordinator, &generateForTest);
+    if (err != OK) return err;
 
-    Formatter out(file);
+    bool isVndk = !generateForTest && isSystemPackage(packageFQName);
+    bool isVndkSp = isVndk && isSystemProcessSupportedPackage(packageFQName);
 
-    out << "// This file is autogenerated by hidl-gen. Do not edit manually.\n\n";
+    std::string packageRoot;
+    err = coordinator->getPackageRoot(packageFQName, &packageRoot);
+    if (err != OK) return err;
 
-    // Rule to generate .hal filegroup
-    out << "filegroup {\n";
-    out.indent();
-    out << "name: \"" << halFilegroupName << "\",\n";
-    out << "srcs: [\n";
-    out.indent();
-    for (const auto &fqName : packageInterfaces) {
-      out << "\"" << fqName.name() << ".hal\",\n";
-    }
-    out.unindent();
-    out << "],\n";
-    out.unindent();
-    out << "}\n\n";
+    out << "// This file is autogenerated by hidl-gen -Landroidbp.\n\n";
 
-    // Rule to generate the C++ source files
-    generateAndroidBpGenSection(
-            out,
-            packageFQName,
-            hidl_gen,
-            coordinator,
-            halFilegroupName,
-            genSourceName,
-            "c++-sources",
-            packageInterfaces,
-            importedPackagesHierarchy,
-            [&pathPrefix](Formatter &out, const FQName &fqName) {
-                if (fqName.name() == "types") {
-                    out << "\"" << pathPrefix << "types.cpp\",\n";
-                } else {
-                    out << "\"" << pathPrefix << fqName.name().substr(1) << "All.cpp\",\n";
-                }
-            });
-
-    // Rule to generate the C++ header files
-    generateAndroidBpGenSection(
-            out,
-            packageFQName,
-            hidl_gen,
-            coordinator,
-            halFilegroupName,
-            genHeaderName,
-            "c++-headers",
-            packageInterfaces,
-            importedPackagesHierarchy,
-            [&pathPrefix](Formatter &out, const FQName &fqName) {
-                out << "\"" << pathPrefix << fqName.name() << ".h\",\n";
-                if (fqName.name() != "types") {
-                    out << "\"" << pathPrefix << fqName.getInterfaceHwName() << ".h\",\n";
-                    out << "\"" << pathPrefix << fqName.getInterfaceStubName() << ".h\",\n";
-                    out << "\"" << pathPrefix << fqName.getInterfaceProxyName() << ".h\",\n";
-                    out << "\"" << pathPrefix << fqName.getInterfacePassthroughName() << ".h\",\n";
-                } else {
-                    out << "\"" << pathPrefix << "hwtypes.h\",\n";
-                }
-            });
-
-    if (isHidlTransportPackage(packageFQName)) {
-        out << "// " << packageFQName.string() << " is exported from libhidltransport\n";
-    } else {
-        generateAndroidBpLibSection(
-            out,
-            false /* generateVendor */,
-            packageFQName,
-            libraryName,
-            genSourceName,
-            genHeaderName,
-            importedPackagesHierarchy);
-
-        // TODO(b/35813011): make all libraries vendor_available
-        // Explicitly create '_vendor' copies of libraries so that
-        // vendor code can link against the extensions. When this is
-        // used, framework code should link against vendor.awesome.foo@1.0
-        // and code on the vendor image should link against
-        // vendor.awesome.foo@1.0_vendor. For libraries with the below extensions,
-        // they will be available even on the generic system image.
-        // Because of this, they should always be referenced without the
-        // '_vendor' name suffix.
-        if (!isSystemPackage(packageFQName)) {
-
-            // Note, not using cc_defaults here since it's already not used and
-            // because generating this libraries will be removed when the VNDK
-            // is enabled (done by the build system itself).
-            out.endl();
-            generateAndroidBpLibSection(
-                out,
-                true /* generateVendor */,
-                packageFQName,
-                libraryName,
-                genSourceName,
-                genHeaderName,
-                importedPackagesHierarchy);
+    out << "hidl_interface ";
+    out.block([&] {
+        out << "name: \"" << makeLibraryName(packageFQName) << "\",\n";
+        if (!coordinator->getOwner().empty()) {
+            out << "owner: \"" << coordinator->getOwner() << "\",\n";
         }
-    }
+        out << "root: \"" << packageRoot << "\",\n";
+        if (isHidlTransportPackage(packageFQName)) {
+            out << "core_interface: true,\n";
+        }
+        if (isVndk) {
+            out << "vndk: ";
+            out.block([&]() {
+                out << "enabled: true,\n";
+                if (isVndkSp) {
+                    out << "support_system_process: true,\n";
+                }
+            }) << ",\n";
+        }
+        (out << "srcs: [\n").indent([&] {
+           for (const auto& fqName : packageInterfaces) {
+               out << "\"" << fqName.name() << ".hal\",\n";
+           }
+        }) << "],\n";
+        if (!importedPackagesHierarchy.empty()) {
+            (out << "interfaces: [\n").indent([&] {
+               for (const auto& fqName : importedPackagesHierarchy) {
+                   out << "\"" << fqName.string() << "\",\n";
+               }
+            }) << "],\n";
+        }
+        if (typesAST != nullptr) {
+            (out << "types: [\n").indent([&] {
+                std::vector<NamedType *> subTypes = typesAST->getRootScope()->getSubTypes();
+                std::sort(
+                        subTypes.begin(),
+                        subTypes.end(),
+                        [](const NamedType *a, const NamedType *b) -> bool {
+                            return a->fqName() < b->fqName();
+                        });
+
+                for (const auto &type : subTypes) {
+                    if (type->isTypeDef()) {
+                        continue;
+                    }
+
+                    out << "\"" << type->localName() << "\",\n";
+                }
+            }) << "],\n";
+        }
+        // Explicity call this out for developers.
+        out << "gen_java: " << (genJavaLibrary ? "true" : "false") << ",\n";
+        if (genJavaConstants) {
+            out << "gen_java_constants: true,\n";
+        }
+   }).endl().endl();
 
     return OK;
 }
 
-static status_t generateAndroidBpImplForPackage(
-        const FQName &packageFQName,
-        const char *,
-        Coordinator *coordinator,
-        const std::string &outputDir) {
-
+static status_t generateAndroidBpImplForPackage(Formatter& out, const FQName& packageFQName,
+                                                const Coordinator* coordinator) {
     const std::string libraryName = makeLibraryName(packageFQName) + "-impl";
 
     std::vector<FQName> packageInterfaces;
@@ -987,23 +678,28 @@
         ast->getImportedPackages(&importedPackages);
     }
 
-    std::string path = outputDir + "Android.bp";
-
-    CHECK(Coordinator::MakeParentHierarchy(path));
-    FILE *file = fopen(path.c_str(), "w");
-
-    if (file == NULL) {
-        return -errno;
-    }
-
-    Formatter out(file);
-
     out << "cc_library_shared {\n";
     out.indent([&] {
-        out << "name: \"" << libraryName << "\",\n"
-            << "relative_install_path: \"hw\",\n"
-            << "proprietary: true,\n"
-            << "srcs: [\n";
+        out << "// FIXME: this should only be -impl for a passthrough hal.\n"
+            << "// In most cases, to convert this to a binderized implementation, you should:\n"
+            << "// - change '-impl' to '-service' here and make it a cc_binary instead of a\n"
+            << "//   cc_library_shared.\n"
+            << "// - add a *.rc file for this module.\n"
+            << "// - delete HIDL_FETCH_I* functions.\n"
+            << "// - call configureRpcThreadpool and registerAsService on the instance.\n"
+            << "// You may also want to append '-impl/-service' with a specific identifier like\n"
+            << "// '-vendor' or '-<hardware identifier>' etc to distinguish it.\n";
+        out << "name: \"" << libraryName << "\",\n";
+        if (!coordinator->getOwner().empty()) {
+            out << "owner: \"" << coordinator->getOwner() << "\",\n";
+        }
+        out << "relative_install_path: \"hw\",\n";
+        if (coordinator->getOwner().empty()) {
+            out << "// FIXME: this should be 'vendor: true' for modules that will eventually be\n"
+                   "// on AOSP.\n";
+        }
+        out << "proprietary: true,\n";
+        out << "srcs: [\n";
         out.indent([&] {
             for (const auto &fqName : packageInterfaces) {
                 if (fqName.name() == "types") {
@@ -1035,8 +731,8 @@
     return OK;
 }
 
-bool validateForSource(
-        const FQName &fqName, const std::string &language) {
+bool validateForSource(const FQName& fqName, const Coordinator* coordinator,
+                       const std::string& language) {
     if (fqName.package().empty()) {
         fprintf(stderr, "ERROR: Expecting package name\n");
         return false;
@@ -1066,14 +762,29 @@
         return true;
     }
 
+    if (language == "java") {
+        bool isJavaCompatible;
+        status_t err = isPackageJavaCompatible(fqName, coordinator, &isJavaCompatible);
+        if (err != OK) return false;
+
+        if (!isJavaCompatible) {
+            fprintf(stderr,
+                    "ERROR: %s is not Java compatible. The Java backend"
+                    " does NOT support union types nor native handles. "
+                    "In addition, vectors of arrays are limited to at most "
+                    "one-dimensional arrays and vectors of {vectors,interfaces} are"
+                    " not supported.\n",
+                    fqName.string().c_str());
+            return false;
+        }
+    }
+
     return true;
 }
 
-OutputHandler::GenerationFunction generateExportHeaderForPackage(bool forJava) {
-    return [forJava](const FQName &packageFQName,
-                     const char * /* hidl_gen */,
-                     Coordinator *coordinator,
-                     const std::string &outputPath) -> status_t {
+FileGenerator::GenerationFunction generateExportHeaderForPackage(bool forJava) {
+    return [forJava](Formatter& out, const FQName& packageFQName,
+                     const Coordinator* coordinator) -> status_t {
         CHECK(packageFQName.isValid()
                 && !packageFQName.package().empty()
                 && !packageFQName.version().empty()
@@ -1108,29 +819,18 @@
             return OK;
         }
 
-        std::string path = outputPath;
-
-        if (forJava) {
-            path.append(coordinator->convertPackageRootToPath(packageFQName));
-
-            path.append(coordinator->getPackagePath(
-                        packageFQName, true /* relative */, true /* sanitized */));
-
-            path.append("Constants.java");
+        if (!out.isValid()) {
+            return UNKNOWN_ERROR;
         }
 
-        CHECK(Coordinator::MakeParentHierarchy(path));
-        FILE *file = fopen(path.c_str(), "w");
-
-        if (file == nullptr) {
-            return -errno;
-        }
-
-        Formatter out(file);
+        std::string packagePath;
+        err = coordinator->getPackagePath(packageFQName, false /* relative */,
+                                          false /* sanitized */, &packagePath);
+        if (err != OK) return err;
 
         out << "// This file is autogenerated by hidl-gen. Do not edit manually.\n"
             << "// Source: " << packageFQName.string() << "\n"
-            << "// Root: " << coordinator->getPackageRootOption(packageFQName) << "\n\n";
+            << "// Location: " << packagePath << "\n\n";
 
         std::string guard;
         if (forJava) {
@@ -1167,164 +867,336 @@
     };
 }
 
-static status_t generateHashOutput(const FQName &fqName,
-        const char* /*hidl_gen*/,
-        Coordinator *coordinator,
-        const std::string & /*outputDir*/) {
+static status_t generateHashOutput(Formatter& out, const FQName& fqName,
+                                   const Coordinator* coordinator) {
+    CHECK(fqName.isFullyQualified());
 
-    status_t err;
-    std::vector<FQName> packageInterfaces;
+    AST* ast = coordinator->parse(fqName, {} /* parsed */,
+                                  Coordinator::Enforce::NO_HASH /* enforcement */);
 
-    if (fqName.isFullyQualified()) {
-        packageInterfaces = {fqName};
-    } else {
-        err = coordinator->appendPackageInterfacesToVector(
-                fqName, &packageInterfaces);
-        if (err != OK) {
-            return err;
-        }
+    if (ast == NULL) {
+        fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
+
+        return UNKNOWN_ERROR;
     }
 
-    for (const auto &currentFqName : packageInterfaces) {
-        AST* ast = coordinator->parse(currentFqName, {} /* parsed */,
-                                      Coordinator::Enforce::NO_HASH /* enforcement */);
-
-        if (ast == NULL) {
-            fprintf(stderr,
-                    "ERROR: Could not parse %s. Aborting.\n",
-                    currentFqName.string().c_str());
-
-            return UNKNOWN_ERROR;
-        }
-
-        printf("%s %s\n",
-                Hash::getHash(ast->getFilename()).hexString().c_str(),
-                currentFqName.string().c_str());
-    }
+    out << Hash::getHash(ast->getFilename()).hexString() << " " << fqName.string() << "\n";
 
     return OK;
 }
 
-static std::vector<OutputHandler> formats = {
-    {"check",
-     "Parses the interface to see if valid but doesn't write any files.",
-     OutputHandler::NOT_NEEDED /* mOutputMode */,
-     validateForSource,
-     generationFunctionForFileOrPackage("check")
-    },
+template <typename T>
+std::vector<T> operator+(const std::vector<T>& lhs, const std::vector<T>& rhs) {
+    std::vector<T> ret;
+    ret.reserve(lhs.size() + rhs.size());
+    ret.insert(ret.begin(), lhs.begin(), lhs.end());
+    ret.insert(ret.end(), rhs.begin(), rhs.end());
+    return ret;
+}
 
-    {"c++",
-     "(internal) (deprecated) Generates C++ interface files for talking to HIDL interfaces.",
-     OutputHandler::NEEDS_DIR /* mOutputMode */,
-     validateForSource,
-     generationFunctionForFileOrPackage("c++")
+// clang-format off
+static const std::vector<FileGenerator> kCppHeaderFormats = {
+    {
+        FileGenerator::alwaysGenerate,
+        [](const FQName& fqName) { return fqName.name() + ".h"; },
+        astGenerationFunction(&AST::generateInterfaceHeader),
     },
-
-    {"c++-headers",
-     "(internal) Generates C++ headers for interface files for talking to HIDL interfaces.",
-     OutputHandler::NEEDS_DIR /* mOutputMode */,
-     validateForSource,
-     generationFunctionForFileOrPackage("c++-headers")
+    {
+        FileGenerator::alwaysGenerate,
+        [](const FQName& fqName) {
+            return fqName.isInterfaceName() ? fqName.getInterfaceHwName() + ".h" : "hwtypes.h";
+        },
+        astGenerationFunction(&AST::generateHwBinderHeader),
     },
-
-    {"c++-sources",
-     "(internal) Generates C++ sources for interface files for talking to HIDL interfaces.",
-     OutputHandler::NEEDS_DIR /* mOutputMode */,
-     validateForSource,
-     generationFunctionForFileOrPackage("c++-sources")
+    {
+        FileGenerator::generateForInterfaces,
+        [](const FQName& fqName) { return fqName.getInterfaceStubName() + ".h"; },
+        astGenerationFunction(&AST::generateStubHeader),
     },
-
-    {"export-header",
-     "Generates a header file from @export enumerations to help maintain legacy code.",
-     OutputHandler::NEEDS_FILE /* mOutputMode */,
-     validateIsPackage,
-     generateExportHeaderForPackage(false /* forJava */)
+    {
+        FileGenerator::generateForInterfaces,
+        [](const FQName& fqName) { return fqName.getInterfaceProxyName() + ".h"; },
+        astGenerationFunction(&AST::generateProxyHeader),
     },
-
-    {"c++-impl",
-     "Generates boilerplate implementation of a hidl interface in C++ (for convenience).",
-     OutputHandler::NEEDS_DIR /* mOutputMode */,
-     validateForSource,
-     generationFunctionForFileOrPackage("c++-impl")
-    },
-    {"c++-impl-headers",
-     "c++-impl but headers only",
-     OutputHandler::NEEDS_DIR /* mOutputMode */,
-     validateForSource,
-     generationFunctionForFileOrPackage("c++-impl-headers")
-    },
-    {"c++-impl-sources",
-     "c++-impl but sources only",
-     OutputHandler::NEEDS_DIR /* mOutputMode */,
-     validateForSource,
-     generationFunctionForFileOrPackage("c++-impl-sources")
-    },
-
-    {"java",
-     "(internal) Generates Java library for talking to HIDL interfaces in Java.",
-     OutputHandler::NEEDS_DIR /* mOutputMode */,
-     validateForSource,
-     generationFunctionForFileOrPackage("java")
-    },
-
-    {"java-constants",
-     "(internal) Like export-header but for Java (always created by -Lmakefile if @export exists).",
-     OutputHandler::NEEDS_DIR /* mOutputMode */,
-     validateIsPackage,
-     generateExportHeaderForPackage(true /* forJava */)
-    },
-
-    {"vts",
-     "(internal) Generates vts proto files for use in vtsd.",
-     OutputHandler::NEEDS_DIR /* mOutputMode */,
-     validateForSource,
-     generationFunctionForFileOrPackage("vts")
-    },
-
-    {"makefile",
-     "(internal) Generates makefiles for -Ljava and -Ljava-constants.",
-     OutputHandler::NEEDS_SRC /* mOutputMode */,
-     validateIsPackage,
-     generateMakefileForPackage,
-    },
-
-    {"androidbp",
-     "(internal) Generates Soong bp files for -Lc++-headers and -Lc++-sources.",
-     OutputHandler::NEEDS_SRC /* mOutputMode */,
-     validateIsPackage,
-     generateAndroidBpForPackage,
-    },
-
-    {"androidbp-impl",
-     "Generates boilerplate bp files for implementation created with -Lc++-impl.",
-     OutputHandler::NEEDS_DIR /* mOutputMode */,
-     validateIsPackage,
-     generateAndroidBpImplForPackage,
-    },
-
-    {"hash",
-     "Prints hashes of interface in `current.txt` format to standard out.",
-     OutputHandler::NOT_NEEDED /* mOutputMode */,
-     validateForSource,
-     generateHashOutput,
+    {
+        FileGenerator::generateForInterfaces,
+        [](const FQName& fqName) { return fqName.getInterfacePassthroughName() + ".h"; },
+        astGenerationFunction(&AST::generatePassthroughHeader),
     },
 };
 
+static const std::vector<FileGenerator> kCppSourceFormats = {
+    {
+        FileGenerator::alwaysGenerate,
+        [](const FQName& fqName) {
+            return fqName.isInterfaceName() ? fqName.getInterfaceBaseName() + "All.cpp" : "types.cpp";
+        },
+        astGenerationFunction(&AST::generateCppSource),
+    },
+};
+
+static const std::vector<FileGenerator> kCppImplHeaderFormats = {
+    {
+        FileGenerator::generateForInterfaces,
+        [](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".h"; },
+        astGenerationFunction(&AST::generateCppImplHeader),
+    },
+};
+
+static const std::vector<FileGenerator> kCppImplSourceFormats = {
+    {
+        FileGenerator::generateForInterfaces,
+        [](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".cpp"; },
+        astGenerationFunction(&AST::generateCppImplSource),
+    },
+};
+
+static const std::vector<FileGenerator> kCppAdapterHeaderFormats = {
+    {
+        FileGenerator::alwaysGenerate,
+        [](const FQName& fqName) {
+            return fqName.isInterfaceName() ? fqName.getInterfaceAdapterName() + ".h" : "Atypes.h";
+        },
+        astGenerationFunction(&AST::generateCppAdapterHeader),
+    },
+};
+
+static const std::vector<FileGenerator> kCppAdapterSourceFormats = {
+    {
+        FileGenerator::alwaysGenerate,
+        [](const FQName& fqName) {
+            return fqName.isInterfaceName() ? fqName.getInterfaceAdapterName() + ".cpp" : "Atypes.cpp";
+        },
+        astGenerationFunction(&AST::generateCppAdapterSource),
+    },
+};
+
+static const std::vector<OutputHandler> kFormats = {
+    {
+        "check",
+        "Parses the interface to see if valid but doesn't write any files.",
+        OutputMode::NOT_NEEDED,
+        Coordinator::Location::STANDARD_OUT,
+        GenerationGranularity::PER_FILE,
+        validateForSource,
+        {
+            {
+                FileGenerator::alwaysGenerate,
+                nullptr /* filename for fqname */,
+                astGenerationFunction(),
+            },
+        },
+    },
+    {
+        "c++",
+        "(internal) (deprecated) Generates C++ interface files for talking to HIDL interfaces.",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::GEN_OUTPUT,
+        GenerationGranularity::PER_FILE,
+        validateForSource,
+        kCppHeaderFormats + kCppSourceFormats,
+    },
+    {
+        "c++-headers",
+        "(internal) Generates C++ headers for interface files for talking to HIDL interfaces.",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::GEN_OUTPUT,
+        GenerationGranularity::PER_FILE,
+        validateForSource,
+        kCppHeaderFormats,
+    },
+    {
+        "c++-sources",
+        "(internal) Generates C++ sources for interface files for talking to HIDL interfaces.",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::GEN_OUTPUT,
+        GenerationGranularity::PER_FILE,
+        validateForSource,
+        kCppSourceFormats,
+    },
+    {
+        "export-header",
+        "Generates a header file from @export enumerations to help maintain legacy code.",
+        OutputMode::NEEDS_FILE,
+        Coordinator::Location::DIRECT,
+        GenerationGranularity::PER_PACKAGE,
+        validateIsPackage,
+        {singleFileGenerator("", generateExportHeaderForPackage(false /* forJava */))}
+    },
+    {
+        "c++-impl",
+        "Generates boilerplate implementation of a hidl interface in C++ (for convenience).",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::DIRECT,
+        GenerationGranularity::PER_FILE,
+        validateForSource,
+        kCppImplHeaderFormats + kCppImplSourceFormats,
+    },
+    {
+        "c++-impl-headers",
+        "c++-impl but headers only",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::DIRECT,
+        GenerationGranularity::PER_FILE,
+        validateForSource,
+        kCppImplHeaderFormats,
+    },
+    {
+        "c++-impl-sources",
+        "c++-impl but sources only",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::DIRECT,
+        GenerationGranularity::PER_FILE,
+        validateForSource,
+        kCppImplSourceFormats,
+    },
+    {
+        "c++-adapter",
+        "Takes a x.(y+n) interface and mocks an x.y interface.",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::GEN_OUTPUT,
+        GenerationGranularity::PER_FILE,
+        validateForSource,
+        kCppAdapterHeaderFormats + kCppAdapterSourceFormats,
+    },
+    {
+        "c++-adapter-headers",
+        "c++-adapter but helper headers only",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::GEN_OUTPUT,
+        GenerationGranularity::PER_FILE,
+        validateForSource,
+        kCppAdapterHeaderFormats,
+    },
+    {
+        "c++-adapter-sources",
+        "c++-adapter but helper sources only",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::GEN_OUTPUT,
+        GenerationGranularity::PER_FILE,
+        validateForSource,
+        kCppAdapterSourceFormats,
+    },
+    {
+        "c++-adapter-main",
+        "c++-adapter but the adapter binary source only",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::DIRECT,
+        GenerationGranularity::PER_PACKAGE,
+        validateIsPackage,
+        {singleFileGenerator("main.cpp", generateAdapterMainSource)},
+    },
+    {
+        "java",
+        "(internal) Generates Java library for talking to HIDL interfaces in Java.",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::GEN_SANITIZED,
+        GenerationGranularity::PER_TYPE,
+        validateForSource,
+        {
+            {
+                FileGenerator::alwaysGenerate,
+                [](const FQName& fqName) {
+                    return StringHelper::LTrim(fqName.name(), "types.") + ".java";
+                },
+                generateJavaForPackage,
+            },
+        }
+    },
+    {
+        "java-constants",
+        "(internal) Like export-header but for Java (always created by -Lmakefile if @export exists).",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::GEN_SANITIZED,
+        GenerationGranularity::PER_PACKAGE,
+        validateIsPackage,
+        {singleFileGenerator("Constants.java", generateExportHeaderForPackage(true /* forJava */))}
+    },
+    {
+        "vts",
+        "(internal) Generates vts proto files for use in vtsd.",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::GEN_OUTPUT,
+        GenerationGranularity::PER_FILE,
+        validateForSource,
+        {
+            {
+                FileGenerator::alwaysGenerate,
+                [](const FQName& fqName) {
+                    return fqName.isInterfaceName() ? fqName.getInterfaceBaseName() + ".vts" : "types.vts";
+                },
+                astGenerationFunction(&AST::generateVts),
+            },
+        }
+    },
+    {
+        "makefile",
+        "(removed) Used to generate makefiles for -Ljava and -Ljava-constants.",
+        OutputMode::NEEDS_SRC,
+        Coordinator::Location::PACKAGE_ROOT,
+        GenerationGranularity::PER_PACKAGE,
+        [](const FQName &, const Coordinator*, const std::string &) {
+           fprintf(stderr, "ERROR: makefile output is not supported. Use -Landroidbp for all build file generation.\n");
+           return false;
+        },
+        {},
+    },
+    {
+        "androidbp",
+        "(internal) Generates Soong bp files for -Lc++-headers, -Lc++-sources, -Ljava, -Ljava-constants, and -Lc++-adapter.",
+        OutputMode::NEEDS_SRC,
+        Coordinator::Location::PACKAGE_ROOT,
+        GenerationGranularity::PER_PACKAGE,
+        validateIsPackage,
+        {singleFileGenerator("Android.bp", generateAndroidBpForPackage)},
+    },
+    {
+        "androidbp-impl",
+        "Generates boilerplate bp files for implementation created with -Lc++-impl.",
+        OutputMode::NEEDS_DIR,
+        Coordinator::Location::DIRECT,
+        GenerationGranularity::PER_PACKAGE,
+        validateIsPackage,
+        {singleFileGenerator("Android.bp", generateAndroidBpImplForPackage)},
+    },
+    {
+        "hash",
+        "Prints hashes of interface in `current.txt` format to standard out.",
+        OutputMode::NOT_NEEDED,
+        Coordinator::Location::STANDARD_OUT,
+        GenerationGranularity::PER_FILE,
+        validateForSource,
+        {
+            {
+                FileGenerator::alwaysGenerate,
+                nullptr /* file name for fqName */,
+                generateHashOutput,
+            },
+        }
+    },
+};
+// clang-format on
+
 static void usage(const char *me) {
     fprintf(stderr,
-            "usage: %s [-p <root path>] -o <output path> -L <language> (-r <interface root>)+ [-t] "
-            "fqname+\n",
+            "usage: %s [-p <root path>] -o <output path> -L <language> [-O <owner>] (-r <interface "
+            "root>)+ [-v] [-d <depfile>] FQNAME...\n\n",
             me);
 
+    fprintf(stderr,
+            "Process FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)?, to create output.\n\n");
+
     fprintf(stderr, "         -h: Prints this menu.\n");
     fprintf(stderr, "         -L <language>: The following options are available:\n");
-    for (auto &e : formats) {
+    for (auto& e : kFormats) {
         fprintf(stderr, "            %-16s: %s\n", e.name().c_str(), e.description().c_str());
     }
+    fprintf(stderr, "         -O <owner>: The owner of the module for -Landroidbp(-impl)?.\n");
     fprintf(stderr, "         -o <output path>: Location to output files.\n");
     fprintf(stderr, "         -p <root path>: Android build root, defaults to $ANDROID_BUILD_TOP or pwd.\n");
     fprintf(stderr, "         -r <package:path root>: E.g., android.hardware:hardware/interfaces.\n");
-    fprintf(stderr, "         -t: generate build scripts (Android.bp) for tests.\n");
+    fprintf(stderr, "         -v: verbose output.\n");
+    fprintf(stderr, "         -d <depfile>: location of depfile to write to.\n");
 }
 
 // hidl is intentionally leaky. Turn off LeakSanitizer by default.
@@ -1333,36 +1205,57 @@
 }
 
 int main(int argc, char **argv) {
-    std::string outputPath;
-    std::string rootPath;
-    std::vector<std::string> packageRootPaths;
-    std::vector<std::string> packageRoots;
-
     const char *me = argv[0];
-    OutputHandler *outputFormat = nullptr;
-
     if (argc == 1) {
         usage(me);
         exit(1);
     }
 
+    const OutputHandler* outputFormat = nullptr;
+    Coordinator coordinator;
+    std::string outputPath;
+
     int res;
-    while ((res = getopt(argc, argv, "hp:o:r:L:t")) >= 0) {
+    while ((res = getopt(argc, argv, "hp:o:O:r:L:vd:")) >= 0) {
         switch (res) {
-            case 'p':
-            {
-                rootPath = optarg;
+            case 'p': {
+                if (!coordinator.getRootPath().empty()) {
+                    fprintf(stderr, "ERROR: -p <root path> can only be specified once.\n");
+                    exit(1);
+                }
+                coordinator.setRootPath(optarg);
                 break;
             }
 
-            case 'o':
-            {
+            case 'v': {
+                coordinator.setVerbose(true);
+                break;
+            }
+
+            case 'd': {
+                coordinator.setDepFile(optarg);
+                break;
+            }
+
+            case 'o': {
+                if (!outputPath.empty()) {
+                    fprintf(stderr, "ERROR: -o <output path> can only be specified once.\n");
+                    exit(1);
+                }
                 outputPath = optarg;
                 break;
             }
 
-            case 'r':
-            {
+            case 'O': {
+                if (!coordinator.getOwner().empty()) {
+                    fprintf(stderr, "ERROR: -O <owner> can only be specified once.\n");
+                    exit(1);
+                }
+                coordinator.setOwner(optarg);
+                break;
+            }
+
+            case 'r': {
                 std::string val(optarg);
                 auto index = val.find_first_of(':');
                 if (index == std::string::npos) {
@@ -1370,22 +1263,27 @@
                     exit(1);
                 }
 
-                auto package = val.substr(0, index);
+                auto root = val.substr(0, index);
                 auto path = val.substr(index + 1);
-                packageRootPaths.push_back(path);
-                packageRoots.push_back(package);
+
+                std::string error;
+                status_t err = coordinator.addPackagePath(root, path, &error);
+                if (err != OK) {
+                    fprintf(stderr, "%s\n", error.c_str());
+                    exit(1);
+                }
+
                 break;
             }
 
-            case 'L':
-            {
+            case 'L': {
                 if (outputFormat != nullptr) {
                     fprintf(stderr,
                             "ERROR: only one -L option allowed. \"%s\" already specified.\n",
                             outputFormat->name().c_str());
                     exit(1);
                 }
-                for (auto &e : formats) {
+                for (auto& e : kFormats) {
                     if (e.name() == optarg) {
                         outputFormat = &e;
                         break;
@@ -1400,15 +1298,9 @@
                 break;
             }
 
-            case 't': {
-                generateForTest = true;
-                break;
-            }
-
             case '?':
             case 'h':
-            default:
-            {
+            default: {
                 usage(me);
                 exit(1);
                 break;
@@ -1416,17 +1308,19 @@
         }
     }
 
+    if (coordinator.getRootPath().empty()) {
+        const char* ANDROID_BUILD_TOP = getenv("ANDROID_BUILD_TOP");
+        if (ANDROID_BUILD_TOP != nullptr) {
+            coordinator.setRootPath(ANDROID_BUILD_TOP);
+        }
+    }
+
     if (outputFormat == nullptr) {
         fprintf(stderr,
             "ERROR: no -L option provided.\n");
         exit(1);
     }
 
-    if (generateForTest && outputFormat->name() != "androidbp") {
-        fprintf(stderr, "ERROR: -t option is for -Landroidbp only.\n");
-        exit(1);
-    }
-
     argc -= optind;
     argv += optind;
 
@@ -1436,43 +1330,29 @@
         exit(1);
     }
 
-    if (rootPath.empty()) {
-        const char *ANDROID_BUILD_TOP = getenv("ANDROID_BUILD_TOP");
-
-        if (ANDROID_BUILD_TOP != nullptr) {
-            rootPath = ANDROID_BUILD_TOP;
-        }
-
-        // else default to pwd
-    }
-
-    if (!rootPath.empty() && !StringHelper::EndsWith(rootPath, "/")) {
-        rootPath += "/";
-    }
-
     // Valid options are now in argv[0] .. argv[argc - 1].
 
     switch (outputFormat->mOutputMode) {
-        case OutputHandler::NEEDS_DIR:
-        case OutputHandler::NEEDS_FILE:
-        {
+        case OutputMode::NEEDS_DIR:
+        case OutputMode::NEEDS_FILE: {
             if (outputPath.empty()) {
                 usage(me);
                 exit(1);
             }
 
-            if (outputFormat->mOutputMode == OutputHandler::NEEDS_DIR) {
-                const size_t len = outputPath.size();
-                if (outputPath[len - 1] != '/') {
+            if (outputFormat->mOutputMode == OutputMode::NEEDS_DIR) {
+                if (outputPath.back() != '/') {
                     outputPath += "/";
                 }
             }
             break;
         }
-        case OutputHandler::NEEDS_SRC:
-        {
+        case OutputMode::NEEDS_SRC: {
             if (outputPath.empty()) {
-                outputPath = rootPath;
+                outputPath = coordinator.getRootPath();
+            }
+            if (outputPath.back() != '/') {
+                outputPath += "/";
             }
 
             break;
@@ -1483,33 +1363,38 @@
             break;
     }
 
-    Coordinator coordinator(packageRootPaths, packageRoots, rootPath);
+    coordinator.setOutputPath(outputPath);
+
     coordinator.addDefaultPackagePath("android.hardware", "hardware/interfaces");
     coordinator.addDefaultPackagePath("android.hidl", "system/libhidl/transport");
     coordinator.addDefaultPackagePath("android.frameworks", "frameworks/hardware/interfaces");
     coordinator.addDefaultPackagePath("android.system", "system/hardware/interfaces");
 
     for (int i = 0; i < argc; ++i) {
-        FQName fqName(argv[i]);
-
-        if (!fqName.isValid()) {
-            fprintf(stderr,
-                    "ERROR: Invalid fully-qualified name.\n");
+        FQName fqName;
+        if (!FQName::parse(argv[i], &fqName)) {
+            fprintf(stderr, "ERROR: Invalid fully-qualified name as argument: %s.\n", argv[i]);
             exit(1);
         }
 
-        if (!outputFormat->validate(fqName, outputFormat->name())) {
+        // Dump extra verbose output
+        if (coordinator.isVerbose()) {
+            status_t err =
+                dumpDefinedButUnreferencedTypeNames(fqName.getPackageAndVersion(), &coordinator);
+            if (err != OK) return err;
+        }
+
+        if (!outputFormat->validate(fqName, &coordinator, outputFormat->name())) {
             fprintf(stderr,
                     "ERROR: output handler failed.\n");
             exit(1);
         }
 
-        status_t err =
-            outputFormat->generate(fqName, me, &coordinator, outputPath);
+        status_t err = outputFormat->generate(fqName, &coordinator);
+        if (err != OK) exit(1);
 
-        if (err != OK) {
-            exit(1);
-        }
+        err = outputFormat->writeDepFile(fqName, &coordinator);
+        if (err != OK) exit(1);
     }
 
     return 0;
diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh
new file mode 100755
index 0000000..bb6f7d7
--- /dev/null
+++ b/scripts/run-tests.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+
+# See hal_hidl_gtest.py
+
+THREADS=
+CHECKER=vts_testability_checker
+CHECKER_DEVICE_PATH="/data/local/tmp/${CHECKER}"
+PRINT_COMMANDS=
+
+function run() {
+    if [ "${PRINT_COMMANDS}" = true ] ; then
+        >&2 echo "*** $@"
+    fi
+    $@
+}
+
+function make_modules() {
+    if [ "${THREADS}" != "0" ] ; then
+        run make -j${THREADS} -C ${ANDROID_BUILD_TOP} -f build/core/main.mk $@
+    fi
+}
+
+function push_checker() {
+    run adb push ${OUT}/system/bin/${CHECKER} ${CHECKER_DEVICE_PATH}
+}
+
+function push_test() {
+    local module=$1
+    for test_dir in nativetest nativetest64 ; do
+        local test_file=/data/${test_dir}/${module}/${module}
+        run adb push ${OUT}${test_file} ${test_file}
+    done
+}
+
+function read_checker_output() {
+    python -c 'import json,sys;obj=json.load(sys.stdin);sys.stdout.write("%s\n"%obj["Testable"]);map(lambda i:sys.stdout.write("%s\n"%i),obj["instances"])'
+}
+
+function run_test() {
+    local module=$1
+    local status=0
+
+    for test_dir in nativetest nativetest64 ; do
+        local test_file=/data/${test_dir}/${module}/${module}
+        local interfaces=$(run adb shell ${test_file} --list_registered_services \
+            | sed -n 's/^hal_service: \(.*\)$/\1/p')
+        if [ -z "$interfaces" ]; then
+            run adb shell ${test_file} || status=$?
+        else
+            for interface in ${interfaces} ; do
+                local output=$(run adb shell ${CHECKER_DEVICE_PATH} -c ${interface} | read_checker_output)
+                local testable=$(echo "${output}" | head -n1)
+                local instances=$(echo "${output}" | tail -n+2)
+
+                if [ "${testable}" == "True" ] ; then
+                    for instance in ${instances} ; do
+                        run adb shell ${test_file} --hal_service_instance="${interface}/${instance}" || status=$?
+                    done
+                fi
+            done
+        fi
+    done
+    return ${status}
+}
+
+function usage() {
+    echo "usage: $0 -m <module_name> [-m <module_name>[...]] [-j <jobs>] [-p]"
+    echo "          -m <module_name>: name of test (e.g. VtsHalHealthV2_0TargetTest)"
+    echo "          -p: print commands"
+    echo "          -j <jobs>: # jobs in make. "
+    echo "                     -j0 skips making any modules."
+    echo "                     If not present, use infinite number of jobs."
+
+    exit 1
+}
+
+function main() {
+    local modules=
+
+    while getopts "m:j:p" option ; do
+        case "${option}" in
+            m)
+                [ ! -z ${OPTARG} ] || usage
+                modules="${modules} ${OPTARG}"
+                ;;
+            j)
+                THREADS=${OPTARG}
+                ;;
+            p)
+                PRINT_COMMANDS=true
+                ;;
+            *)
+                usage
+                ;;
+        esac
+    done
+
+    set -e
+    make_modules ${CHECKER} ${modules}
+    run adb root
+    push_checker
+    for module in ${modules} ; do
+        push_test ${module}
+    done
+
+    set +e
+    local status=0
+    for module in ${modules} ; do
+        run_test ${module} || status=$?
+    done
+    return ${status}
+}
+
+main $@
diff --git a/test/Android.bp b/test/Android.bp
deleted file mode 100644
index 08aed77..0000000
--- a/test/Android.bp
+++ /dev/null
@@ -1,80 +0,0 @@
-subdirs = [
-    "error_test",
-    "hash_test",
-    "impl_test",
-    "java_test",
-    "vendor/1.0",
-    "vendor/1.1",
-    "version_test",
-]
-
-cc_test {
-    name: "hidl_test_client",
-    cflags: hidl_flags,
-    srcs: ["hidl_test_client.cpp", "FooCallback.cpp"],
-
-    shared_libs: [
-        "libbase",
-        "liblog",
-        "libcutils",
-        "libhidlbase",
-        "libhidltransport",
-        "libhidlmemory",
-        "libfootest",
-        "libhwbinder",
-        "libpointertest",
-        "libutils",
-        "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",
-        "android.hardware.tests.multithread@1.0",
-        "android.hidl.allocator@1.0",
-        "android.hidl.memory@1.0",
-        "android.hidl.token@1.0",
-    ],
-
-    required: [
-        // Warning, these should never be specified explicitly for anything else
-        // as for a given device, what hal implementation is used is determined
-        // by what is specified/available in a device.mk. However, this test
-        // actually depends on these test implementations (which should never be
-        // included on a device.mk).
-        "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",
-        "android.hardware.tests.multithread@1.0-impl",
-    ],
-}
-
-cc_test {
-    name: "hidl_test_servers",
-    srcs: ["hidl_test_servers.cpp"],
-    gtest: false,
-
-    shared_libs: [
-        "libbase",
-        "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
-        "liblog",
-        "libutils",
-        "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",
-        "android.hardware.tests.multithread@1.0",
-        "android.hidl.memory@1.0",
-        "android.hidl.token@1.0",
-    ],
-}
diff --git a/test/AndroidTest.xml b/test/AndroidTest.xml
deleted file mode 100644
index e78aac5..0000000
--- a/test/AndroidTest.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<configuration description="Config for VTS HIDL unit tests">
-    <option name="config-descriptor:metadata" key="plan" value="vts-library" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
-        <option name="push-group" value="HostDrivenTest.push" />
-        <option name="cleanup" value="true" />
-        <option name="push" value="DATA/nativetest/hidl_test_servers/hidl_test_servers->/data/nativetest/hidl_test_servers/hidl_test_servers" />
-        <option name="push" value="DATA/nativetest64/hidl_test_servers/hidl_test_servers->/data/nativetest64/hidl_test_servers/hidl_test_servers" />
-        <option name="push" value="DATA/nativetest/hidl_test_client/hidl_test_client->/data/nativetest/hidl_test_client/hidl_test_client" />
-        <option name="push" value="DATA/nativetest64/hidl_test_client/hidl_test_client->/data/nativetest64/hidl_test_client/hidl_test_client" />
-        <option name="push" value="DATA/nativetest64/hidl_test_helper->/data/nativetest64/hidl_test_helper" />
-        <option name="push" value="DATA/nativetest64/hidl_test->/data/nativetest64/hidl_test" />
-        <option name="push" value="DATA/lib/libfootest.so->/data/local/tmp/system/lib/libfootest.so" />
-        <option name="push" value="DATA/lib64/libfootest.so->/data/local/tmp/system/lib64/libfootest.so" />
-        <option name="push" value="DATA/lib/libpointertest.so->/data/local/tmp/system/lib/libpointertest.so" />
-        <option name="push" value="DATA/lib64/libpointertest.so->/data/local/tmp/system/lib64/libpointertest.so" />
-        <option name="push" value="DATA/lib/android.hardware.tests.foo@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.foo@1.0.so" />
-        <option name="push" value="DATA/lib64/android.hardware.tests.foo@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.foo@1.0.so" />
-        <option name="push" value="DATA/lib/android.hardware.tests.bar@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.bar@1.0.so" />
-        <option name="push" value="DATA/lib64/android.hardware.tests.bar@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.bar@1.0.so" />
-        <option name="push" value="DATA/lib/android.hardware.tests.baz@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.baz@1.0.so" />
-        <option name="push" value="DATA/lib64/android.hardware.tests.baz@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.baz@1.0.so" />
-        <option name="push" value="DATA/lib/android.hardware.tests.hash@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.hash@1.0.so" />
-        <option name="push" value="DATA/lib64/android.hardware.tests.hash@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.hash@1.0.so" />
-        <option name="push" value="DATA/lib/android.hardware.tests.inheritance@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.inheritance@1.0.so" />
-        <option name="push" value="DATA/lib64/android.hardware.tests.inheritance@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.inheritance@1.0.so" />
-        <option name="push" value="DATA/lib/android.hardware.tests.pointer@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.pointer@1.0.so" />
-        <option name="push" value="DATA/lib64/android.hardware.tests.pointer@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.pointer@1.0.so" />
-        <option name="push" value="DATA/lib/android.hardware.tests.memory@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.memory@1.0.so" />
-        <option name="push" value="DATA/lib64/android.hardware.tests.memory@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.memory@1.0.so" />
-        <option name="push" value="DATA/lib/android.hardware.tests.multithread@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.multithread@1.0.so" />
-        <option name="push" value="DATA/lib64/android.hardware.tests.multithread@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.multithread@1.0.so" />
-        <option name="push" value="DATA/lib/hw/android.hardware.tests.foo@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.foo@1.0-impl.so" />
-        <option name="push" value="DATA/lib64/hw/android.hardware.tests.foo@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.foo@1.0-impl.so" />
-        <option name="push" value="DATA/lib/hw/android.hardware.tests.bar@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.bar@1.0-impl.so" />
-        <option name="push" value="DATA/lib64/hw/android.hardware.tests.bar@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.bar@1.0-impl.so" />
-        <option name="push" value="DATA/lib/hw/android.hardware.tests.baz@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.baz@1.0-impl.so" />
-        <option name="push" value="DATA/lib64/hw/android.hardware.tests.baz@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.baz@1.0-impl.so" />
-        <option name="push" value="DATA/lib/hw/android.hardware.tests.hash@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.hash@1.0-impl.so" />
-        <option name="push" value="DATA/lib64/hw/android.hardware.tests.hash@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.hash@1.0-impl.so" />
-        <option name="push" value="DATA/lib/hw/android.hardware.tests.inheritance@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.inheritance@1.0-impl.so" />
-        <option name="push" value="DATA/lib64/hw/android.hardware.tests.inheritance@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.inheritance@1.0-impl.so" />
-        <option name="push" value="DATA/lib/hw/android.hardware.tests.pointer@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.pointer@1.0-impl.so" />
-        <option name="push" value="DATA/lib64/hw/android.hardware.tests.pointer@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.pointer@1.0-impl.so" />
-        <option name="push" value="DATA/lib/hw/android.hardware.tests.memory@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.memory@1.0-impl.so" />
-        <option name="push" value="DATA/lib64/hw/android.hardware.tests.memory@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.memory@1.0-impl.so" />
-        <option name="push" value="DATA/lib/hw/android.hardware.tests.multithread@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.multithread@1.0-impl.so" />
-        <option name="push" value="DATA/lib64/hw/android.hardware.tests.multithread@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.multithread@1.0-impl.so" />
-    </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.VtsPythonVirtualenvPreparer" />
-    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
-      <option name="test-module-name" value="VtsHidlUnitTests" />
-      <option name="binary-test-type" value="binary_test" />
-      <option name="binary-test-source" value="DATA/nativetest64/hidl_test->/data/nativetest64/hidl_test" />
-      <option name="binary-test-envp" value="VTS_ROOT_PATH=/data/local/tmp" />
-      <option name="binary-test-args" value="-x /data/local/tmp/system/lib" />
-      <option name="binary-test-args" value="-y /data/local/tmp/system/lib64" />
-    </test>
-</configuration>
diff --git a/test/error_test/Android.bp b/test/error_test/Android.bp
index bd5c076..42345b2 100644
--- a/test/error_test/Android.bp
+++ b/test/error_test/Android.bp
@@ -1,51 +1,20 @@
 genrule {
     name: "hidl_error_test_gen",
-    tools: [
-        "hidl-gen",
-    ],
-    cmd: "!($(location hidl-gen) -L check " +
-         "    -r test.error:system/tools/hidl/test/error_test/interface_semicolon" +
-         "    test.error.error@1.0  >/dev/null 2>&1)" +
-         "&&" +
-         "!($(location hidl-gen) -L check " +
-         "    -r test.error:system/tools/hidl/test/error_test/method_ends_in_semicolon" +
-         "    test.error.error@1.0  >/dev/null 2>&1)" +
-         "&&" +
-         "!($(location hidl-gen) -L check " +
-         "    -r test.error:system/tools/hidl/test/error_test/no_data_outside_interface" +
-         "    test.error.error@1.0  >/dev/null 2>&1)" +
-         "&&" +
-         "!($(location hidl-gen) -L check " +
-         "    -r test.error:system/tools/hidl/test/error_test/no_interface_in_types" +
-         "    test.error.error@1.0  >/dev/null 2>&1)" +
-         "&&" +
-         "!($(location hidl-gen) -L check " +
-         "    -r test.error:system/tools/hidl/test/error_test/no_two_interfaces" +
-         "    test.error.error@1.0  >/dev/null 2>&1)" +
-         "&&" +
-         "!($(location hidl-gen) -L check " +
-         "    -r test.error:system/tools/hidl/test/error_test/same_name_interface" +
-         "    test.error.error@1.0  >/dev/null 2>&1)" +
-         "&&" +
-         "!($(location hidl-gen) -L check " +
-         "    -r test.error:system/tools/hidl/test/error_test/same_package_name" +
-         "    test.error.error@1.0  >/dev/null 2>&1)" +
-         "&&" +
+    tools: ["hidl-gen"],
+    tool_files: ["hidl_error_test.sh"],
+    cmd: "$(location hidl_error_test.sh) $(location hidl-gen) &&" +
          "echo 'int main(){return 0;}' > $(genDir)/TODO_b_37575883.cpp",
     out: ["TODO_b_37575883.cpp"],
     srcs: [
-        "interface_semicolon/error/1.0/IFoo.hal",
-        "method_ends_in_semicolon/error/1.0/IFoo.hal",
-        "no_data_outside_interface/error/1.0/IFoo.hal",
-        "no_interface_in_types/error/1.0/IFoo.hal",
-        "no_interface_in_types/error/1.0/types.hal",
-        "no_two_interfaces/error/1.0/IFoo.hal",
-        "same_name_interface/error/1.0/IFoo.hal",
-        "same_package_name/error/1.0/IFoo.hal",
+        "hidl_error_test.sh",
+
+        "**/*.hal",
+        "**/required_error",
     ],
 }
 
 cc_test_host {
     name: "hidl_error_test",
+    cflags: ["-Wall", "-Werror"],
     generated_sources: ["hidl_error_test_gen"],
 }
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/bad_character/1.0/IFoo.hal
similarity index 93%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/bad_character/1.0/IFoo.hal
index d79e6ab..9164ee5 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/bad_character/1.0/IFoo.hal
@@ -13,7 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.bad_character@1.0;
+
+😄
 
 interface IFoo {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/bad_character/1.0/required_error b/test/error_test/bad_character/1.0/required_error
new file mode 100644
index 0000000..ab3b3e7
--- /dev/null
+++ b/test/error_test/bad_character/1.0/required_error
@@ -0,0 +1 @@
+syntax error, unexpected unknown character
\ No newline at end of file
diff --git a/test/error_test/cpp_forward_decl_restriction/1.0/required_error b/test/error_test/cpp_forward_decl_restriction/1.0/required_error
new file mode 100644
index 0000000..0426995
--- /dev/null
+++ b/test/error_test/cpp_forward_decl_restriction/1.0/required_error
@@ -0,0 +1 @@
+C++ forward declaration doesn't support inner types
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/cpp_forward_decl_restriction/1.0/types.hal
similarity index 74%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/cpp_forward_decl_restriction/1.0/types.hal
index d79e6ab..17d2aa9 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/cpp_forward_decl_restriction/1.0/types.hal
@@ -13,7 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.cpp_forward_decl_restriction@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+struct A {
+    // Requires A.B.C to be forward declared in A,
+    // which is not supported in C++.
+    A.B.C foo;
+
+    struct B {
+        struct C {};
+    };
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/doc_comment_ends_block/1.0/IFoo.hal
similarity index 88%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/doc_comment_ends_block/1.0/IFoo.hal
index d79e6ab..26c768e 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/doc_comment_ends_block/1.0/IFoo.hal
@@ -13,7 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.empty_generates@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+    doFoo();
+    /**
+     * Does foo!
+     */
+};
diff --git a/test/error_test/doc_comment_ends_block/1.0/required_error b/test/error_test/doc_comment_ends_block/1.0/required_error
new file mode 100644
index 0000000..94c947a
--- /dev/null
+++ b/test/error_test/doc_comment_ends_block/1.0/required_error
@@ -0,0 +1 @@
+Doc comments must preceed what they describe at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/empty_generates/1.0/IFoo.hal
similarity index 91%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/empty_generates/1.0/IFoo.hal
index d79e6ab..1ad23e0 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/empty_generates/1.0/IFoo.hal
@@ -13,7 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.empty_generates@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+    foo() generates ();
+};
diff --git a/test/error_test/empty_generates/1.0/required_error b/test/error_test/empty_generates/1.0/required_error
new file mode 100644
index 0000000..ce24040
--- /dev/null
+++ b/test/error_test/empty_generates/1.0/required_error
@@ -0,0 +1 @@
+generates clause used without result at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/enum_storage/1.0/IFoo.hal
similarity index 88%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/enum_storage/1.0/IFoo.hal
index d79e6ab..68f17c9 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/enum_storage/1.0/IFoo.hal
@@ -13,7 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.bad_character@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+
+    enum A /* missing storage */ {
+    };
+
+};
+
diff --git a/test/error_test/enum_storage/1.0/required_error b/test/error_test/enum_storage/1.0/required_error
new file mode 100644
index 0000000..aeb0e84
--- /dev/null
+++ b/test/error_test/enum_storage/1.0/required_error
@@ -0,0 +1 @@
+Must explicitly specify enum storage type
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/enum_unique_field_names/1.0/IFoo.hal
similarity index 82%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/enum_unique_field_names/1.0/IFoo.hal
index d79e6ab..69818e8 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/enum_unique_field_names/1.0/IFoo.hal
@@ -13,7 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.enum_unique_field_names@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+    enum E : int32_t {
+        A,
+        B,
+        A,  // duplicates first value name
+    };
+};
diff --git a/test/error_test/enum_unique_field_names/1.0/required_error b/test/error_test/enum_unique_field_names/1.0/required_error
new file mode 100644
index 0000000..26d6385
--- /dev/null
+++ b/test/error_test/enum_unique_field_names/1.0/required_error
@@ -0,0 +1 @@
+Redefinition of value 'A'
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/enum_unique_field_names_extends/1.0/IFoo.hal
similarity index 76%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/enum_unique_field_names_extends/1.0/IFoo.hal
index d79e6ab..a2e4642 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/enum_unique_field_names_extends/1.0/IFoo.hal
@@ -13,7 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.enum_unique_field_names_extends@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+    enum E1 : int32_t {
+        A,
+        B,
+        C,
+    };
+
+    enum E2 : E1 {
+        D,
+        B,  // duplicates E1.B name
+        F,
+    };
+};
diff --git a/test/error_test/enum_unique_field_names_extends/1.0/required_error b/test/error_test/enum_unique_field_names_extends/1.0/required_error
new file mode 100644
index 0000000..dbb4cdb
--- /dev/null
+++ b/test/error_test/enum_unique_field_names_extends/1.0/required_error
@@ -0,0 +1 @@
+Redefinition of value 'B' defined in enum
\ No newline at end of file
diff --git a/test/error_test/hidl_error_test.sh b/test/error_test/hidl_error_test.sh
new file mode 100755
index 0000000..2f5d522
--- /dev/null
+++ b/test/error_test/hidl_error_test.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+if [ $# -ne 1 ]; then
+    echo "usage: hidl_error_test.sh hidl-gen_path"
+    exit 1
+fi
+
+readonly HIDL_GEN_PATH=$1
+readonly HIDL_ERROR_TEST_DIR="$ANDROID_BUILD_TOP/system/tools/hidl/test/error_test"
+
+for dir in $(ls -d $HIDL_ERROR_TEST_DIR/*/); do
+  package=$(basename $dir)
+  output=$($HIDL_GEN_PATH -L check -r test:$HIDL_ERROR_TEST_DIR test.$package@1.0 2>&1)
+  command_fails=$?
+  error=$(cat $HIDL_ERROR_TEST_DIR/$package/1.0/required_error)
+
+  if [[ $error == "" ]]; then
+    echo "error: No required error message specified for $package."
+    echo "$output" | while read line; do echo "test output: $line"; done
+    exit 1
+  fi
+
+  if [ $command_fails -ne 1 ]; then
+    echo "error: $package test did not fail"
+    exit 1
+  fi
+
+  if [[ $output != *$error* ]]; then
+    echo "error: error output for $package does not contain '$error':"
+    echo "$output" | while read line; do echo "test output: $line"; done
+    exit 1
+  fi
+
+done
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/interface_extends_only_interface/1.0/IFoo.hal
similarity index 84%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/interface_extends_only_interface/1.0/IFoo.hal
index d79e6ab..6b18b73 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/interface_extends_only_interface/1.0/IFoo.hal
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.interface_extends_only_interface@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+interface IFoo extends IStruct /* struct, not interface */ {
+};
diff --git a/test/error_test/interface_extends_only_interface/1.0/required_error b/test/error_test/interface_extends_only_interface/1.0/required_error
new file mode 100644
index 0000000..718544b
--- /dev/null
+++ b/test/error_test/interface_extends_only_interface/1.0/required_error
@@ -0,0 +1 @@
+You can only extend interfaces
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/interface_extends_only_interface/1.0/types.hal
similarity index 89%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/interface_extends_only_interface/1.0/types.hal
index d79e6ab..dffd41e 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/interface_extends_only_interface/1.0/types.hal
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.interface_extends_only_interface@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+struct IStruct {
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/interface_no_using_reserved_method_names/1.0/IFoo.hal
similarity index 85%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/interface_no_using_reserved_method_names/1.0/IFoo.hal
index d79e6ab..9b68217 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/interface_no_using_reserved_method_names/1.0/IFoo.hal
@@ -13,7 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.interface_no_using_reserved_method_names@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+    debug(); // duplicates reserved method name
+};
diff --git a/test/error_test/interface_no_using_reserved_method_names/1.0/required_error b/test/error_test/interface_no_using_reserved_method_names/1.0/required_error
new file mode 100644
index 0000000..ec1dc30
--- /dev/null
+++ b/test/error_test/interface_no_using_reserved_method_names/1.0/required_error
@@ -0,0 +1 @@
+Redefinition of reserved method 'debug'
\ No newline at end of file
diff --git a/test/error_test/interface_semicolon/error/1.0/IFoo.hal b/test/error_test/interface_semicolon/1.0/IFoo.hal
similarity index 94%
rename from test/error_test/interface_semicolon/error/1.0/IFoo.hal
rename to test/error_test/interface_semicolon/1.0/IFoo.hal
index 8222665..f334052 100644
--- a/test/error_test/interface_semicolon/error/1.0/IFoo.hal
+++ b/test/error_test/interface_semicolon/1.0/IFoo.hal
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.interface_semicolon@1.0;
 
 interface IFoo {
-} // <- missing semicolon
\ No newline at end of file
+} // <- missing semicolon
diff --git a/test/error_test/interface_semicolon/1.0/required_error b/test/error_test/interface_semicolon/1.0/required_error
new file mode 100644
index 0000000..4301bae
--- /dev/null
+++ b/test/error_test/interface_semicolon/1.0/required_error
@@ -0,0 +1 @@
+missing ; at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/interface_unique_method_names/1.0/IFoo.hal
similarity index 83%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/interface_unique_method_names/1.0/IFoo.hal
index d79e6ab..9a39b97 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/interface_unique_method_names/1.0/IFoo.hal
@@ -13,7 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.interface_unique_method_names@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+    foo(int32_t arg);
+    foo(IFoo arg); // duplicates first method name
+};
diff --git a/test/error_test/interface_unique_method_names/1.0/required_error b/test/error_test/interface_unique_method_names/1.0/required_error
new file mode 100644
index 0000000..42c134b
--- /dev/null
+++ b/test/error_test/interface_unique_method_names/1.0/required_error
@@ -0,0 +1 @@
+Redefinition of method 'foo' at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/types.hal b/test/error_test/interface_unique_method_names_inheritance/1.0/IBar.hal
similarity index 87%
copy from test/error_test/no_interface_in_types/error/1.0/types.hal
copy to test/error_test/interface_unique_method_names_inheritance/1.0/IBar.hal
index a246ef8..25ac710 100644
--- a/test/error_test/no_interface_in_types/error/1.0/types.hal
+++ b/test/error_test/interface_unique_method_names_inheritance/1.0/IBar.hal
@@ -13,8 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.interface_unique_method_names_inheritance@1.0;
 
-// can't have interface in types file
 interface IBar {
-};
\ No newline at end of file
+    foo(int32_t arg);
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/interface_unique_method_names_inheritance/1.0/IFoo.hal
similarity index 78%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/interface_unique_method_names_inheritance/1.0/IFoo.hal
index d79e6ab..6e47cef 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/interface_unique_method_names_inheritance/1.0/IFoo.hal
@@ -13,7 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.interface_unique_method_names_inheritance@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+import IBar;
+
+interface IFoo extends IBar {
+    foo(IFoo arg); // duplicates method defined in IBar name
+};
diff --git a/test/error_test/interface_unique_method_names_inheritance/1.0/required_error b/test/error_test/interface_unique_method_names_inheritance/1.0/required_error
new file mode 100644
index 0000000..a76d410
--- /dev/null
+++ b/test/error_test/interface_unique_method_names_inheritance/1.0/required_error
@@ -0,0 +1 @@
+Redefinition of method 'foo'
\ No newline at end of file
diff --git a/test/error_test/method_ends_in_semicolon/error/1.0/IFoo.hal b/test/error_test/method_ends_in_semicolon/1.0/IFoo.hal
similarity index 93%
rename from test/error_test/method_ends_in_semicolon/error/1.0/IFoo.hal
rename to test/error_test/method_ends_in_semicolon/1.0/IFoo.hal
index e8ebca6..1814314 100644
--- a/test/error_test/method_ends_in_semicolon/error/1.0/IFoo.hal
+++ b/test/error_test/method_ends_in_semicolon/1.0/IFoo.hal
@@ -13,8 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.method_ends_in_semicolon@1.0;
 
 interface IFoo {
     foo() generates (int32_t bar)
-};
\ No newline at end of file
+};
diff --git a/test/error_test/method_ends_in_semicolon/1.0/required_error b/test/error_test/method_ends_in_semicolon/1.0/required_error
new file mode 100644
index 0000000..4301bae
--- /dev/null
+++ b/test/error_test/method_ends_in_semicolon/1.0/required_error
@@ -0,0 +1 @@
+missing ; at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/missing_variable_type/1.0/IFoo.hal
similarity index 85%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/missing_variable_type/1.0/IFoo.hal
index d79e6ab..b73bc84 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/missing_variable_type/1.0/IFoo.hal
@@ -13,7 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.missing_variable_type@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+    struct Struct { uint32_t a; };
+    foo() generates (Struct);
+};
diff --git a/test/error_test/missing_variable_type/1.0/required_error b/test/error_test/missing_variable_type/1.0/required_error
new file mode 100644
index 0000000..54573c6
--- /dev/null
+++ b/test/error_test/missing_variable_type/1.0/required_error
@@ -0,0 +1 @@
+is missing a variable name
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_declaration_1/1.0/IFoo.hal
similarity index 86%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_declaration_1/1.0/IFoo.hal
index d79e6ab..80b4938 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_declaration_1/1.0/IFoo.hal
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.no_cyclic_declaration_1@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+interface IFoo extends IFoo /* extends itself */ {
+};
diff --git a/test/error_test/no_cyclic_declaration_1/1.0/required_error b/test/error_test/no_cyclic_declaration_1/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_declaration_1/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_declaration_2/1.0/IFoo.hal
similarity index 84%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_declaration_2/1.0/IFoo.hal
index d79e6ab..9477305 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_declaration_2/1.0/IFoo.hal
@@ -13,7 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.no_cyclic_declaration_2@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+    struct A {
+        B b;
+    };
+    struct B {
+        A a;
+    };
+};
diff --git a/test/error_test/no_cyclic_declaration_2/1.0/required_error b/test/error_test/no_cyclic_declaration_2/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_declaration_2/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_cyclic_declaration_3/1.0/required_error b/test/error_test/no_cyclic_declaration_3/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_declaration_3/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_declaration_3/1.0/types.hal
similarity index 89%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_declaration_3/1.0/types.hal
index d79e6ab..3f78c51 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_declaration_3/1.0/types.hal
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.no_cyclic_declaration_3@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+typedef A B;
+typedef B A;
diff --git a/test/error_test/no_cyclic_declaration_4/1.0/required_error b/test/error_test/no_cyclic_declaration_4/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_declaration_4/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_declaration_4/1.0/types.hal
similarity index 88%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_declaration_4/1.0/types.hal
index d79e6ab..32a7683 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_declaration_4/1.0/types.hal
@@ -13,7 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.no_cyclic_declaration_4@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+typedef S SS;
+struct S {
+    SS foo;
+};
diff --git a/test/error_test/no_cyclic_expressions_1/1.0/required_error b/test/error_test/no_cyclic_expressions_1/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_expressions_1/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_expressions_1/1.0/types.hal
similarity index 86%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_expressions_1/1.0/types.hal
index d79e6ab..e46a150 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_expressions_1/1.0/types.hal
@@ -13,7 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.no_cyclic_expressions_1@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+enum E : int32_t {
+    A = D,
+    B,
+    C,
+    D
+};
diff --git a/test/error_test/no_cyclic_expressions_2/1.0/required_error b/test/error_test/no_cyclic_expressions_2/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_expressions_2/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_expressions_2/1.0/types.hal
similarity index 84%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_expressions_2/1.0/types.hal
index d79e6ab..e67a774 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_expressions_2/1.0/types.hal
@@ -13,7 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.no_cyclic_expressions_2@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+enum X : int32_t {
+    A = Y:D,
+    B,
+};
+
+enum Y : X {
+    C,
+    D,
+};
diff --git a/test/error_test/no_cyclic_expressions_3/1.0/required_error b/test/error_test/no_cyclic_expressions_3/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_expressions_3/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_expressions_3/1.0/types.hal
similarity index 89%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_expressions_3/1.0/types.hal
index d79e6ab..c544609 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_expressions_3/1.0/types.hal
@@ -13,7 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.no_cyclic_expressions_3@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+enum E : int32_t {
+    A = A
+};
diff --git a/test/error_test/no_data_outside_interface/error/1.0/IFoo.hal b/test/error_test/no_data_outside_interface/1.0/IFoo.hal
similarity index 93%
rename from test/error_test/no_data_outside_interface/error/1.0/IFoo.hal
rename to test/error_test/no_data_outside_interface/1.0/IFoo.hal
index 4a5ab96..73681b2 100644
--- a/test/error_test/no_data_outside_interface/error/1.0/IFoo.hal
+++ b/test/error_test/no_data_outside_interface/1.0/IFoo.hal
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.no_data_outside_interface@1.0;
 
 struct Bar { // can only have this in types.hal files
 };
 
 interface IFoo {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/no_data_outside_interface/1.0/required_error b/test/error_test/no_data_outside_interface/1.0/required_error
new file mode 100644
index 0000000..497ccd4
--- /dev/null
+++ b/test/error_test/no_data_outside_interface/1.0/required_error
@@ -0,0 +1 @@
+declares types rather than the expected interface type 'IFoo'
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_interface_in_types/1.0/IFoo.hal
similarity index 93%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_interface_in_types/1.0/IFoo.hal
index d79e6ab..a994968 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_interface_in_types/1.0/IFoo.hal
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.no_interface_in_types@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/no_interface_in_types/1.0/required_error b/test/error_test/no_interface_in_types/1.0/required_error
new file mode 100644
index 0000000..3fe7eaf
--- /dev/null
+++ b/test/error_test/no_interface_in_types/1.0/required_error
@@ -0,0 +1 @@
+declares an interface 'IBar' instead of the expected types common to the package
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/types.hal b/test/error_test/no_interface_in_types/1.0/types.hal
similarity index 94%
rename from test/error_test/no_interface_in_types/error/1.0/types.hal
rename to test/error_test/no_interface_in_types/1.0/types.hal
index a246ef8..a2c49f8 100644
--- a/test/error_test/no_interface_in_types/error/1.0/types.hal
+++ b/test/error_test/no_interface_in_types/1.0/types.hal
@@ -13,8 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.no_interface_in_types@1.0;
 
 // can't have interface in types file
 interface IBar {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_two_interfaces/1.0/IFoo.hal
similarity index 91%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_two_interfaces/1.0/IFoo.hal
index d79e6ab..eb416d3 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_two_interfaces/1.0/IFoo.hal
@@ -13,7 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.no_two_interfaces@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+};
+
+interface IBar {
+};
diff --git a/test/error_test/no_two_interfaces/1.0/required_error b/test/error_test/no_two_interfaces/1.0/required_error
new file mode 100644
index 0000000..497ccd4
--- /dev/null
+++ b/test/error_test/no_two_interfaces/1.0/required_error
@@ -0,0 +1 @@
+declares types rather than the expected interface type 'IFoo'
\ No newline at end of file
diff --git a/test/error_test/no_two_interfaces/error/1.0/IFoo.hal b/test/error_test/no_two_interfaces/error/1.0/IFoo.hal
deleted file mode 100644
index db27507..0000000
--- a/test/error_test/no_two_interfaces/error/1.0/IFoo.hal
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.error.error@1.0;
-
-interface IFoo {
-};
-
-// can only have one interface per file
-interface IBar {
-};
\ No newline at end of file
diff --git a/test/error_test/same_name_interface/error/1.0/IFoo.hal b/test/error_test/references_broken_package/1.0/IBar.hal
similarity index 78%
copy from test/error_test/same_name_interface/error/1.0/IFoo.hal
copy to test/error_test/references_broken_package/1.0/IBar.hal
index df7806c..27dbdeb 100644
--- a/test/error_test/same_name_interface/error/1.0/IFoo.hal
+++ b/test/error_test/references_broken_package/1.0/IBar.hal
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.references_broken_package@1.0;
 
-// Must be called IFoo
+import test.references_broken_package.bad_package@1.0::IFoo;
+
 interface IBar {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/references_broken_package/1.0/required_error b/test/error_test/references_broken_package/1.0/required_error
new file mode 100644
index 0000000..4301bae
--- /dev/null
+++ b/test/error_test/references_broken_package/1.0/required_error
@@ -0,0 +1 @@
+missing ; at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/references_broken_package/bad_package/1.0/IFoo.hal
similarity index 80%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/references_broken_package/bad_package/1.0/IFoo.hal
index d79e6ab..14d4189 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/references_broken_package/bad_package/1.0/IFoo.hal
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.references_broken_package.bad_package@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+} // no semicolon -> error
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/references_broken_package/bad_package/1.0/types.hal
similarity index 81%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/references_broken_package/bad_package/1.0/types.hal
index d79e6ab..ea7ed6e 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/references_broken_package/bad_package/1.0/types.hal
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.references_broken_package.bad_package@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+struct IFoo {};
\ No newline at end of file
diff --git a/test/error_test/same_name_interface/error/1.0/IFoo.hal b/test/error_test/same_name_interface/1.0/IFoo.hal
similarity index 94%
rename from test/error_test/same_name_interface/error/1.0/IFoo.hal
rename to test/error_test/same_name_interface/1.0/IFoo.hal
index df7806c..71a0671 100644
--- a/test/error_test/same_name_interface/error/1.0/IFoo.hal
+++ b/test/error_test/same_name_interface/1.0/IFoo.hal
@@ -13,8 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.same_name_interface@1.0;
 
 // Must be called IFoo
 interface IBar {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/same_name_interface/1.0/required_error b/test/error_test/same_name_interface/1.0/required_error
new file mode 100644
index 0000000..1a46ce9
--- /dev/null
+++ b/test/error_test/same_name_interface/1.0/required_error
@@ -0,0 +1 @@
+does not declare interface type 'IFoo'
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/same_package_name/1.0/IFoo.hal
similarity index 95%
rename from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
rename to test/error_test/same_package_name/1.0/IFoo.hal
index d79e6ab..60702d9 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/same_package_name/1.0/IFoo.hal
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.notpackage@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/same_package_name/1.0/required_error b/test/error_test/same_package_name/1.0/required_error
new file mode 100644
index 0000000..d18a773
--- /dev/null
+++ b/test/error_test/same_package_name/1.0/required_error
@@ -0,0 +1 @@
+does not match expected package and/or version
\ No newline at end of file
diff --git a/test/error_test/same_package_name/error/1.0/IFoo.hal b/test/error_test/same_package_name/error/1.0/IFoo.hal
deleted file mode 100644
index efc71b2..0000000
--- a/test/error_test/same_package_name/error/1.0/IFoo.hal
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.error.notpackage@1.0;
-
-interface IFoo {
-};
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/scope_unique_type_names/1.0/IFoo.hal
similarity index 84%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/scope_unique_type_names/1.0/IFoo.hal
index d79e6ab..815c8a2 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/scope_unique_type_names/1.0/IFoo.hal
@@ -13,7 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.scope_unique_type_names@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+    struct S {};
+    enum S : int32_t {};  // duplicates first struct name
+};
diff --git a/test/error_test/scope_unique_type_names/1.0/required_error b/test/error_test/scope_unique_type_names/1.0/required_error
new file mode 100644
index 0000000..f876888
--- /dev/null
+++ b/test/error_test/scope_unique_type_names/1.0/required_error
@@ -0,0 +1 @@
+A type named 'S' is already declared in the scope at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/struct_unique_field_names/1.0/IFoo.hal
similarity index 82%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/struct_unique_field_names/1.0/IFoo.hal
index d79e6ab..d5c07cd 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/struct_unique_field_names/1.0/IFoo.hal
@@ -13,7 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package test.struct_unique_field_names@1.0;
 
 interface IFoo {
-};
\ No newline at end of file
+    struct S {
+        int32_t a;
+        int32_t a;  // duplicates first field name
+    };
+};
diff --git a/test/error_test/struct_unique_field_names/1.0/required_error b/test/error_test/struct_unique_field_names/1.0/required_error
new file mode 100644
index 0000000..0aca5dc
--- /dev/null
+++ b/test/error_test/struct_unique_field_names/1.0/required_error
@@ -0,0 +1 @@
+Redefinition of field 'a'
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/export_test/1.0/IFoo.hal
similarity index 86%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/export_test/1.0/IFoo.hal
index d79e6ab..adde1da 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/export_test/1.0/IFoo.hal
@@ -13,7 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package export@1.0;
 
 interface IFoo {
+    @export
+    enum S : uint32_t {
+        X,
+        Y,
+        Z,
+    };
 };
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/export_test/1.0/types.hal
similarity index 69%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/export_test/1.0/types.hal
index d79e6ab..d58d097 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/export_test/1.0/types.hal
@@ -13,7 +13,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+package export@1.0;
 
-interface IFoo {
+@export
+enum NoArgs : uint32_t {
+    A = 1,
+};
+
+@export(name="")
+enum NoName : uint32_t {
+    B = 2,
+};
+
+@export(value_prefix="prefix")
+enum ValueSuffix : uint32_t {
+    C = 3,
+};
+
+@export(value_suffix="suffix")
+enum ValuePrefix : uint32_t {
+    D = 4,
 };
\ No newline at end of file
diff --git a/test/export_test/Android.bp b/test/export_test/Android.bp
new file mode 100644
index 0000000..5d7bead
--- /dev/null
+++ b/test/export_test/Android.bp
@@ -0,0 +1,22 @@
+genrule {
+    name: "hidl_export_test_gen-headers",
+    tools: [
+        "hidl-gen",
+    ],
+    srcs: [
+        "1.0/IFoo.hal",
+        "1.0/types.hal"
+    ],
+    cmd: "$(location hidl-gen) -o $(genDir)/export-base.h -Lexport-header " +
+         "-rexport:system/tools/hidl/test/export_test export@1.0",
+    out: [
+        "export-base.h"
+    ],
+}
+
+cc_test_library {
+    name: "hidl_export_test",
+    cflags: ["-Wall", "-Werror"],
+    generated_headers: ["hidl_export_test_gen-headers"],
+    srcs: ["test.cpp"],
+}
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/export_test/test.cpp
similarity index 60%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/export_test/test.cpp
index d79e6ab..91cebff 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/export_test/test.cpp
@@ -13,7 +13,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+#include <export-base.h>
+
+// From IFoo.hal
+
+static_assert(X == 0, "");
+static_assert(Y == 1, "");
+static_assert(Z == 2, "");
+
+typedef S SExists;
+
+// From types.hal
+
+static_assert(B == 2, "");
+enum NoName {};  // causes error if declared already
+
+static_assert(prefixC == 3, "");
+typedef ValueSuffix ValueSuffixExists;
+
+static_assert(Dsuffix == 4, "");
+typedef ValuePrefix ValuePrefixExists;
diff --git a/test/hash_test/Android.bp b/test/hash_test/Android.bp
index 89c403b..5f9f248 100644
--- a/test/hash_test/Android.bp
+++ b/test/hash_test/Android.bp
@@ -31,5 +31,6 @@
 
 cc_test_host {
     name: "hidl_hash_test",
+    cflags: ["-Wall", "-Werror"],
     generated_sources: ["hidl_hash_test_gen"],
 }
diff --git a/test/hash_test/good/current.txt b/test/hash_test/good/current.txt
index b6e7514..ff4b669 100644
--- a/test/hash_test/good/current.txt
+++ b/test/hash_test/good/current.txt
@@ -1 +1 @@
-b19939ecb4f877820df49b684f3164f0a3f9aa18743a3521f3bd04e4a06fed64 test.hash.hash@1.0::IHash
+b19939ecb4f877820df49b684f3164f0a3f9aa18743a3521f3bd04e4a06fed64 test.hash.hash@1.0::IHash # comments are fine too
diff --git a/test/hidl_test.h b/test/hidl_test.h
deleted file mode 100644
index 3eeebaf..0000000
--- a/test/hidl_test.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef HIDL_TEST_H_
-#define HIDL_TEST_H_
-
-#define EACH_SERVER(THING)                  \
-    do {                                    \
-        THING<IMemoryTest>("memory");       \
-        THING<IChild>("child");             \
-        THING<IParent>("parent");           \
-        THING<IFetcher>("fetcher");         \
-        THING<IBar>("foo");                 \
-        THING<IHash>("default");            \
-        THING<IGraph>("graph");             \
-        THING<IPointer>("pointer");         \
-        THING<IMultithread>("multithread"); \
-    } while (false)
-
-#endif  // HIDL_TEST_H_
\ No newline at end of file
diff --git a/test/hidl_test/Android.bp b/test/hidl_test/Android.bp
new file mode 100644
index 0000000..73110a6
--- /dev/null
+++ b/test/hidl_test/Android.bp
@@ -0,0 +1,77 @@
+cc_defaults {
+    name: "hidl_test_client-defaults",
+    defaults: ["hidl-gen-defaults"],
+
+    shared_libs: [
+        "android.hidl.memory@1.0",
+        "android.hidl.memory.token@1.0",
+        "android.hidl.token@1.0",
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "libhidlmemory",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libutils",
+    ],
+
+    // Allow dlsym'ing self for statically linked passthrough implementations
+    ldflags: ["-rdynamic"],
+
+    // These are static libs only for testing purposes and portability. Shared
+    // libs should be used on device.
+    static_libs: [
+        "libfootest",
+        "libpointertest",
+        "android.hardware.tests.expression@1.0",
+        "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",
+        "android.hardware.tests.multithread@1.0",
+        "android.hardware.tests.trie@1.0",
+    ],
+
+    // impls should never be static, these are used only for testing purposes
+    // and test portability since this test pairs with specific hal
+    // implementations
+    whole_static_libs: [
+        "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",
+        "android.hardware.tests.multithread@1.0-impl",
+        "android.hardware.tests.trie@1.0-impl",
+    ],
+
+    group_static_libs: true,
+}
+
+cc_test {
+    name: "hidl_test_client",
+    defaults: ["hidl_test_client-defaults"],
+
+    srcs: [
+        "hidl_test_client.cpp",
+        "FooCallback.cpp",
+        "static_test.cpp"
+    ],
+
+    shared_libs: [
+        "android.hidl.allocator@1.0",
+    ],
+}
+
+cc_test {
+    name: "hidl_test_servers",
+    defaults: ["hidl_test_client-defaults"],
+    srcs: ["hidl_test_servers.cpp"],
+    gtest: false,
+}
diff --git a/test/Android.mk b/test/hidl_test/Android.mk
similarity index 77%
rename from test/Android.mk
rename to test/hidl_test/Android.mk
index ff0001c..f328223 100644
--- a/test/Android.mk
+++ b/test/hidl_test/Android.mk
@@ -25,12 +25,15 @@
 LOCAL_MODULE := hidl_test
 LOCAL_MODULE_CLASS := NATIVE_TESTS
 LOCAL_SRC_FILES := hidl_test
-LOCAL_REQUIRED_MODULES :=                       \
-    hidl_test_client                            \
-    hidl_test_servers                           \
-    hidl_test_client_32                         \
-    hidl_test_servers_32                        \
-    hidl_test_helper
+
+LOCAL_REQUIRED_MODULES := \
+    hidl_test_client \
+    hidl_test_helper \
+    hidl_test_servers
+
+LOCAL_REQUIRED_MODULES_arm64 := hidl_test_servers_32 hidl_test_client_32
+LOCAL_REQUIRED_MODULES_mips64 := hidl_test_servers_32 hidl_test_client_32
+LOCAL_REQUIRED_MODULES_x86_64 := hidl_test_servers_32 hidl_test_client_32
 
 include $(BUILD_PREBUILT)
 
@@ -39,5 +42,3 @@
 LOCAL_MODULE := VtsHidlUnitTests
 VTS_CONFIG_SRC_DIR := system/tools/hidl/tests
 -include test/vts/tools/build/Android.host_config.mk
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/test/hidl_test/AndroidTest.xml b/test/hidl_test/AndroidTest.xml
new file mode 100644
index 0000000..6633942
--- /dev/null
+++ b/test/hidl_test/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for VTS HIDL unit tests">
+    <option name="config-descriptor:metadata" key="plan" value="vts-library" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+        <option name="push-group" value="HostDrivenTest.push" />
+        <option name="cleanup" value="true" />
+        <option name="push" value="DATA/nativetest/hidl_test_servers/hidl_test_servers->/data/nativetest/hidl_test_servers/hidl_test_servers" />
+        <option name="push" value="DATA/nativetest64/hidl_test_servers/hidl_test_servers->/data/nativetest64/hidl_test_servers/hidl_test_servers" />
+        <option name="push" value="DATA/nativetest/hidl_test_client/hidl_test_client->/data/nativetest/hidl_test_client/hidl_test_client" />
+        <option name="push" value="DATA/nativetest64/hidl_test_client/hidl_test_client->/data/nativetest64/hidl_test_client/hidl_test_client" />
+        <option name="push" value="DATA/nativetest64/hidl_test_helper->/data/nativetest64/hidl_test_helper" />
+    </target_preparer>
+    <multi_target_preparer class="com.android.tradefed.targetprep.VtsPythonVirtualenvPreparer" />
+    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+      <option name="test-module-name" value="VtsHidlUnitTests" />
+      <option name="binary-test-type" value="binary_test" />
+      <option name="binary-test-source" value="DATA/nativetest64/hidl_test->/data/nativetest64/hidl_test" />
+      <option name="test-timeout" value="1m" />
+    </test>
+</configuration>
diff --git a/test/FooCallback.cpp b/test/hidl_test/FooCallback.cpp
similarity index 100%
rename from test/FooCallback.cpp
rename to test/hidl_test/FooCallback.cpp
diff --git a/test/FooCallback.h b/test/hidl_test/FooCallback.h
similarity index 100%
rename from test/FooCallback.h
rename to test/hidl_test/FooCallback.h
diff --git a/test/hidl_test b/test/hidl_test/hidl_test
similarity index 100%
rename from test/hidl_test
rename to test/hidl_test/hidl_test
diff --git a/test/hidl_test/hidl_test.h b/test/hidl_test/hidl_test.h
new file mode 100644
index 0000000..0ad17ef
--- /dev/null
+++ b/test/hidl_test/hidl_test.h
@@ -0,0 +1,40 @@
+#ifndef HIDL_TEST_H_
+#define HIDL_TEST_H_
+
+#include <android/hardware/tests/bar/1.0/IBar.h>
+#include <android/hardware/tests/hash/1.0/IHash.h>
+#include <android/hardware/tests/inheritance/1.0/IChild.h>
+#include <android/hardware/tests/inheritance/1.0/IFetcher.h>
+#include <android/hardware/tests/inheritance/1.0/IParent.h>
+#include <android/hardware/tests/memory/1.0/IMemoryTest.h>
+#include <android/hardware/tests/multithread/1.0/IMultithread.h>
+#include <android/hardware/tests/pointer/1.0/IGraph.h>
+#include <android/hardware/tests/pointer/1.0/IPointer.h>
+#include <android/hardware/tests/trie/1.0/ITrie.h>
+
+template <template <typename Type> class Service>
+void runOnEachServer(void) {
+    using ::android::hardware::tests::bar::V1_0::IBar;
+    using ::android::hardware::tests::hash::V1_0::IHash;
+    using ::android::hardware::tests::inheritance::V1_0::IChild;
+    using ::android::hardware::tests::inheritance::V1_0::IFetcher;
+    using ::android::hardware::tests::inheritance::V1_0::IParent;
+    using ::android::hardware::tests::memory::V1_0::IMemoryTest;
+    using ::android::hardware::tests::multithread::V1_0::IMultithread;
+    using ::android::hardware::tests::pointer::V1_0::IGraph;
+    using ::android::hardware::tests::pointer::V1_0::IPointer;
+    using ::android::hardware::tests::trie::V1_0::ITrie;
+
+    Service<IMemoryTest>::run("memory");
+    Service<IChild>::run("child");
+    Service<IParent>::run("parent");
+    Service<IFetcher>::run("fetcher");
+    Service<IBar>::run("foo");
+    Service<IHash>::run("default");
+    Service<IGraph>::run("graph");
+    Service<IPointer>::run("pointer");
+    Service<IMultithread>::run("multithread");
+    Service<ITrie>::run("trie");
+}
+
+#endif  // HIDL_TEST_H_
diff --git a/test/hidl_test_client.cpp b/test/hidl_test/hidl_test_client.cpp
similarity index 80%
rename from test/hidl_test_client.cpp
rename to test/hidl_test/hidl_test_client.cpp
index a8e7793..718a32c 100644
--- a/test/hidl_test_client.cpp
+++ b/test/hidl_test/hidl_test_client.cpp
@@ -10,11 +10,11 @@
 
 #include <android/hidl/allocator/1.0/IAllocator.h>
 #include <android/hidl/memory/1.0/IMemory.h>
-
+#include <android/hidl/memory/token/1.0/IMemoryToken.h>
 #include <android/hidl/token/1.0/ITokenManager.h>
 
-#include <android/hardware/tests/bar/1.0/BpHwBar.h>
 #include <android/hardware/tests/bar/1.0/BnHwBar.h>
+#include <android/hardware/tests/bar/1.0/BpHwBar.h>
 #include <android/hardware/tests/bar/1.0/IBar.h>
 #include <android/hardware/tests/bar/1.0/IComplicated.h>
 #include <android/hardware/tests/bar/1.0/IImportRules.h>
@@ -32,6 +32,7 @@
 #include <android/hardware/tests/multithread/1.0/IMultithread.h>
 #include <android/hardware/tests/pointer/1.0/IGraph.h>
 #include <android/hardware/tests/pointer/1.0/IPointer.h>
+#include <android/hardware/tests/trie/1.0/ITrie.h>
 
 #include <gtest/gtest.h>
 #if GTEST_IS_THREADSAFE
@@ -51,9 +52,12 @@
 #include <fstream>
 #include <future>
 #include <mutex>
+#include <random>
 #include <set>
 #include <sstream>
 #include <thread>
+#include <type_traits>
+#include <unordered_set>
 #include <utility>
 #include <vector>
 
@@ -62,6 +66,7 @@
 
 #include <hidl/ServiceManagement.h>
 #include <hidl/Status.h>
+#include <hidlmemory/HidlMemoryToken.h>
 #include <hidlmemory/mapping.h>
 
 #include <utils/Condition.h>
@@ -102,11 +107,14 @@
 using ::android::hardware::tests::pointer::V1_0::IPointer;
 using ::android::hardware::tests::memory::V1_0::IMemoryTest;
 using ::android::hardware::tests::multithread::V1_0::IMultithread;
+using ::android::hardware::tests::trie::V1_0::ITrie;
+using ::android::hardware::tests::trie::V1_0::TrieNode;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_death_recipient;
 using ::android::hardware::hidl_memory;
+using ::android::hardware::HidlMemory;
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
 using ::android::hidl::allocator::V1_0::IAllocator;
@@ -114,6 +122,8 @@
 using ::android::hidl::manager::V1_1::IServiceManager;
 using ::android::hidl::manager::V1_0::IServiceNotification;
 using ::android::hidl::memory::V1_0::IMemory;
+using ::android::hidl::memory::token::V1_0::IMemoryToken;
+using ::android::hidl::memory::block::V1_0::MemoryBlock;
 using ::android::hidl::token::V1_0::ITokenManager;
 using ::android::sp;
 using ::android::wp;
@@ -127,16 +137,8 @@
 using ::android::ONEWAY_TOLERANCE_NS;
 using std::to_string;
 
-bool isLibraryOpen(const std::string &lib) {
-    std::ifstream ifs("/proc/self/maps");
-    for (std::string line; std::getline(ifs, line);) {
-        if (line.size() >= lib.size() && line.substr(line.size() - lib.size()) == lib) {
-            return true;
-        }
-    }
-
-    return false;
-}
+template <typename T>
+using hidl_enum_iterator = ::android::hardware::hidl_enum_iterator<T>;
 
 template <typename T>
 static inline ::testing::AssertionResult isOk(const ::android::hardware::Return<T> &ret) {
@@ -268,67 +270,6 @@
     int32_t mCookie;
 };
 
-// Ensure (statically) that the types in IImportRules resolves to the correct types by
-// overriding the methods with fully namespaced types as arguments.
-struct MyImportRules : public ::android::hardware::tests::bar::V1_0::IImportRules {
-    Return<void> rule0a(
-            const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
-        return Void();
-    }
-
-    Return<void> rule0a1(
-            const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
-        return Void();
-    }
-
-    Return<void> rule0b(
-            const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
-        return Void();
-    }
-
-    Return<void> rule0c(const ::android::hardware::tests::foo::V1_0::Outer&) override {
-        return Void();
-    }
-
-    Return<void> rule0d(const ::android::hardware::tests::foo::V1_0::Outer&) override {
-        return Void();
-    }
-
-    Return<void> rule0e(
-            const ::android::hardware::tests::bar::V1_0::IImportRules::Outer::Inner&) override {
-        return Void();
-    }
-
-    Return<void> rule0f(
-            const ::android::hardware::tests::bar::V1_0::IImportRules::Outer::Inner&) override {
-        return Void();
-    }
-
-    Return<void> rule0g(const ::android::hardware::tests::foo::V1_0::Outer::Inner&) override {
-        return Void();
-    }
-
-    Return<void> rule0h(const ::android::hardware::tests::foo::V1_0::Outer::Inner&) override {
-        return Void();
-    }
-
-    Return<void> rule1a(const ::android::hardware::tests::bar::V1_0::Def&) override {
-        return Void();
-    }
-
-    Return<void> rule1b(const ::android::hardware::tests::foo::V1_0::Def&) override {
-        return Void();
-    }
-
-    Return<void> rule2a(const ::android::hardware::tests::foo::V1_0::Unrelated&) override {
-        return Void();
-    }
-
-    Return<void> rule2b(const sp<::android::hardware::tests::foo::V1_0::IFooCallback>&) override {
-        return Void();
-    }
-};
-
 struct ServiceNotification : public IServiceNotification {
     std::mutex mutex;
     std::condition_variable condition;
@@ -373,6 +314,7 @@
     sp<IPointer> pointerInterface;
     sp<IPointer> validationPointerInterface;
     sp<IMultithread> multithreadInterface;
+    sp<ITrie> trieInterface;
     TestMode mode;
     bool enableDelayMeasurementTests;
     HidlEnvironment(TestMode mode, bool enableDelayMeasurementTests) :
@@ -434,6 +376,10 @@
             IMultithread::getService("multithread", mode == PASSTHROUGH /* getStub */);
         ASSERT_NE(multithreadInterface, nullptr);
         ASSERT_EQ(multithreadInterface->isRemote(), mode == BINDERIZED);
+
+        trieInterface = ITrie::getService("trie", mode == PASSTHROUGH /* getStub */);
+        ASSERT_NE(trieInterface, nullptr);
+        ASSERT_EQ(trieInterface->isRemote(), mode == BINDERIZED);
     }
 
     virtual void SetUp() {
@@ -456,6 +402,7 @@
     sp<IGraph> graphInterface;
     sp<IPointer> pointerInterface;
     sp<IPointer> validationPointerInterface;
+    sp<ITrie> trieInterface;
     TestMode mode = TestMode::PASSTHROUGH;
 
     virtual void SetUp() override {
@@ -471,24 +418,12 @@
         graphInterface = gHidlEnvironment->graphInterface;
         pointerInterface = gHidlEnvironment->pointerInterface;
         validationPointerInterface = gHidlEnvironment->validationPointerInterface;
+        trieInterface = gHidlEnvironment->trieInterface;
         mode = gHidlEnvironment->mode;
         ALOGI("Test setup complete");
     }
 };
 
-TEST_F(HidlTest, PreloadTest) {
-    // in passthrough mode, this will already be opened
-    if (mode == BINDERIZED) {
-        using android::hardware::preloadPassthroughService;
-
-        static const std::string kLib = "android.hardware.tests.inheritance@1.0-impl.so";
-
-        EXPECT_FALSE(isLibraryOpen(kLib));
-        preloadPassthroughService<IParent>();
-        EXPECT_TRUE(isLibraryOpen(kLib));
-    }
-}
-
 TEST_F(HidlTest, ToStringTest) {
     using namespace android::hardware;
 
@@ -531,6 +466,38 @@
     EXPECT_NE(nullptr, IFoo::getService("\n", true /* getStub */).get());
 }
 
+TEST_F(HidlTest, EnumIteratorTest) {
+    using Empty = ::android::hardware::tests::foo::V1_0::EnumIterators::Empty;
+    using Grandchild = ::android::hardware::tests::foo::V1_0::EnumIterators::Grandchild;
+    using SkipsValues = ::android::hardware::tests::foo::V1_0::EnumIterators::SkipsValues;
+    using MultipleValues = ::android::hardware::tests::foo::V1_0::EnumIterators::MultipleValues;
+
+    for (const auto value : hidl_enum_iterator<Empty>()) {
+        (void)value;
+        EXPECT_TRUE(false) << "Empty iterator should not iterate";
+    }
+
+    auto it1 = hidl_enum_iterator<Grandchild>().begin();
+    EXPECT_EQ(Grandchild::A, *it1++);
+    EXPECT_EQ(Grandchild::B, *it1++);
+    EXPECT_EQ(hidl_enum_iterator<Grandchild>().end(), it1);
+
+    auto it2 = hidl_enum_iterator<SkipsValues>().begin();
+    EXPECT_EQ(SkipsValues::A, *it2++);
+    EXPECT_EQ(SkipsValues::B, *it2++);
+    EXPECT_EQ(SkipsValues::C, *it2++);
+    EXPECT_EQ(SkipsValues::D, *it2++);
+    EXPECT_EQ(SkipsValues::E, *it2++);
+    EXPECT_EQ(hidl_enum_iterator<SkipsValues>().end(), it2);
+
+    auto it3 = hidl_enum_iterator<MultipleValues>().begin();
+    EXPECT_EQ(MultipleValues::A, *it3++);
+    EXPECT_EQ(MultipleValues::B, *it3++);
+    EXPECT_EQ(MultipleValues::C, *it3++);
+    EXPECT_EQ(MultipleValues::D, *it3++);
+    EXPECT_EQ(hidl_enum_iterator<MultipleValues>().end(), it3);
+}
+
 TEST_F(HidlTest, EnumToStringTest) {
     using namespace std::string_literals;
     using ::android::hardware::tests::foo::V1_0::toString;
@@ -541,10 +508,20 @@
     EXPECT_EQ(toString(static_cast<IFoo::BitField>(IFoo::BitField::V0 | IFoo::BitField::V2)), "0x5"s)
             << "Invalid enum isn't stringified correctly.";
     // dump bitfields
-    EXPECT_EQ(toString<IFoo::BitField>(0 | IFoo::BitField::V0), "V0 (0x1)"s);
-    EXPECT_EQ(toString<IFoo::BitField>(0 | IFoo::BitField::V0 | IFoo::BitField::V2), "V0 | V2 (0x5)"s);
-    EXPECT_EQ(toString<IFoo::BitField>(0xF), "V0 | V1 | V2 | V3 | VALL (0xf)"s);
-    EXPECT_EQ(toString<IFoo::BitField>(0xFF), "V0 | V1 | V2 | V3 | VALL | 0xf0 (0xff)"s);
+    EXPECT_EQ(toString<IFoo::BitField>((uint8_t)0 | IFoo::BitField::V0), "V0 (0x1)"s);
+    EXPECT_EQ(toString<IFoo::BitField>((uint8_t)0 | IFoo::BitField::V0 | IFoo::BitField::V2),
+              "V0 | V2 (0x5)"s);
+    EXPECT_EQ(toString<IFoo::BitField>((uint8_t)0xF), "V0 | V1 | V2 | V3 | VALL (0xf)"s);
+    EXPECT_EQ(toString<IFoo::BitField>((uint8_t)0xFF), "V0 | V1 | V2 | V3 | VALL | 0xf0 (0xff)"s);
+
+    // inheritance
+    using Parent = ::android::hardware::tests::foo::V1_0::EnumIterators::Parent;
+    using EmptyChild = ::android::hardware::tests::foo::V1_0::EnumIterators::EmptyChild;
+    using Grandchild = ::android::hardware::tests::foo::V1_0::EnumIterators::Grandchild;
+    EXPECT_EQ(toString(Parent::A), "A"s);
+    EXPECT_EQ(toString(EmptyChild::A), "A"s);
+    EXPECT_EQ(toString(Grandchild::A), "A"s);
+    EXPECT_EQ(toString(Grandchild::B), "B"s);
 }
 
 TEST_F(HidlTest, PingTest) {
@@ -560,17 +537,24 @@
 }
 
 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};
+    static constexpr uint64_t kHashSize = 32u;
+    // unreleased interface has an empty hash
+    uint8_t ihash[kHashSize] = {0};
+    uint8_t ibase[kHashSize] = {189, 218, 182, 24,  77,  122, 52,  109, 166, 160, 125,
+                                192, 130, 140, 241, 154, 105, 111, 76,  170, 54,  17,
+                                197, 31,  46,  20,  86,  90,  20,  180, 15,  217};
     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!";
-        }));
+    EXPECT_OK(service->getHashChain([&](const auto& chain) {
+        ASSERT_EQ(chain.size(), 2u);
+        EXPECT_EQ(chain[0].size(), kHashSize);
+        EXPECT_ARRAYEQ(ihash, chain[0], kHashSize);
+        EXPECT_EQ(chain[1].size(), kHashSize);
+        EXPECT_ARRAYEQ(ibase, chain[1], kHashSize);
+    }));
+    EXPECT_OK(manager->getHashChain([&](const auto& managerChain) {
+        EXPECT_EQ(managerChain[managerChain.size() - 1].size(), kHashSize);
+        EXPECT_ARRAYEQ(ibase, managerChain[managerChain.size() - 1], kHashSize)
+            << "Hash for IBase doesn't match!";
     }));
 }
 
@@ -624,137 +608,165 @@
     }));
 }
 
-// passthrough TODO(b/31959402)
 TEST_F(HidlTest, ServiceListByInterfaceTest) {
-    if (mode == BINDERIZED) {
-        EXPECT_OK(manager->listByInterface(IParent::descriptor,
-            [](const hidl_vec<hidl_string> &registered) {
-                std::set<std::string> registeredSet;
-
-                for (size_t i = 0; i < registered.size(); i++) {
-                    registeredSet.insert(registered[i]);
-                }
-
-                std::set<std::string> activeSet = {
-                    "parent", "child"
-                };
-                std::set<std::string> difference;
-                std::set_difference(activeSet.begin(), activeSet.end(),
-                                    registeredSet.begin(), registeredSet.end(),
-                                    std::inserter(difference, difference.begin()));
-
-                EXPECT_EQ(difference.size(), 0u) << "service(s) not registered " << to_string(difference);
-            }));
+    if (mode != BINDERIZED) {
+        // passthrough service manager does not know about services
+        return;
     }
+
+    EXPECT_OK(
+        manager->listByInterface(IParent::descriptor, [](const hidl_vec<hidl_string>& registered) {
+            std::set<std::string> registeredSet;
+
+            for (size_t i = 0; i < registered.size(); i++) {
+                registeredSet.insert(registered[i]);
+            }
+
+            std::set<std::string> activeSet = {"parent", "child"};
+            std::set<std::string> difference;
+            std::set_difference(activeSet.begin(), activeSet.end(), registeredSet.begin(),
+                                registeredSet.end(), std::inserter(difference, difference.begin()));
+
+            EXPECT_EQ(difference.size(), 0u)
+                << "service(s) not registered " << to_string(difference);
+        }));
 }
 
-// passthrough TODO(b/31959402)
-TEST_F(HidlTest, ServiceParentTest) {
-    if (mode == BINDERIZED) {
-        sp<IParent> parent = IParent::getService("child");
+TEST_F(HidlTest, SubInterfaceServiceRegistrationTest) {
+    using ::android::hardware::interfacesEqual;
 
-        EXPECT_NE(parent, nullptr);
-    }
+    const std::string kInstanceName = "no-matter-what-it-is";
+    const std::string kOtherName = "something-different";
+
+    sp<IChild> child = new SimpleChild();
+    sp<IParent> parent = new SimpleParent();
+
+    EXPECT_EQ(::android::OK, child->registerAsService(kInstanceName));
+    EXPECT_EQ(::android::OK, child->registerAsService(kOtherName));
+
+    EXPECT_TRUE(interfacesEqual(child, IChild::getService(kInstanceName)));
+    EXPECT_TRUE(interfacesEqual(child, IParent::getService(kInstanceName)));
+
+    EXPECT_EQ(::android::OK, parent->registerAsService(kInstanceName));
+
+    // FALSE since passthrough HAL will return an instance
+    // since binderized instance is nullptr
+    EXPECT_FALSE(interfacesEqual(parent, IChild::getService(kInstanceName)));
+    EXPECT_TRUE(interfacesEqual(parent, IParent::getService(kInstanceName)));
+
+    // other instance name is unchanged
+    EXPECT_TRUE(interfacesEqual(child, IChild::getService(kOtherName)));
+    EXPECT_TRUE(interfacesEqual(child, IParent::getService(kOtherName)));
 }
 
-// passthrough TODO(b/31959402)
 TEST_F(HidlTest, ServiceNotificationTest) {
-    if (mode == BINDERIZED) {
-        ServiceNotification *notification = new ServiceNotification();
-
-        std::string instanceName = "test-instance";
-        EXPECT_TRUE(IParent::registerForNotifications(instanceName, notification));
-
-        EXPECT_EQ(::android::OK, (new SimpleChild())->registerAsService(instanceName));
-        EXPECT_EQ(::android::OK, (new SimpleParent())->registerAsService(instanceName));
-
-        std::unique_lock<std::mutex> lock(notification->mutex);
-
-        notification->condition.wait_for(
-                lock,
-                std::chrono::milliseconds(2),
-                [&notification]() {
-                   return notification->getRegistrations().size() >= 2;
-                });
-
-        std::vector<std::string> registrations = notification->getRegistrations();
-
-        EXPECT_EQ(registrations.size(), 2u);
-
-        EXPECT_EQ(to_string(registrations.data(), registrations.size()),
-                  std::string("['") + IParent::descriptor + "/" + instanceName +
-                             "', '" + IParent::descriptor + "/" + instanceName + "']");
+    if (mode != BINDERIZED) {
+        // service notifications aren't supported in passthrough mode
+        return;
     }
+
+    ServiceNotification* notification = new ServiceNotification();
+
+    std::string instanceName = "test-instance";
+    EXPECT_TRUE(IParent::registerForNotifications(instanceName, notification));
+
+    EXPECT_EQ(::android::OK, (new SimpleChild())->registerAsService(instanceName));
+    EXPECT_EQ(::android::OK, (new SimpleParent())->registerAsService(instanceName));
+
+    std::unique_lock<std::mutex> lock(notification->mutex);
+
+    notification->condition.wait_for(lock, std::chrono::milliseconds(2), [&notification]() {
+        return notification->getRegistrations().size() >= 2;
+    });
+
+    std::vector<std::string> registrations = notification->getRegistrations();
+
+    EXPECT_EQ(registrations.size(), 2u);
+
+    EXPECT_EQ(to_string(registrations.data(), registrations.size()),
+              std::string("['") + IParent::descriptor + "/" + instanceName + "', '" +
+                  IParent::descriptor + "/" + instanceName + "']");
 }
 
 TEST_F(HidlTest, ServiceUnregisterTest) {
-    if (mode == BINDERIZED) {
-        const std::string instance = "some-instance-name";
+    const std::string instance = "some-instance-name";
 
-        sp<ServiceNotification> sNotification = new ServiceNotification();
+    sp<ServiceNotification> sNotification = new ServiceNotification();
 
-        // unregister all
-        EXPECT_TRUE(IParent::registerForNotifications(instance, sNotification));
-        EXPECT_TRUE(manager->unregisterForNotifications("", "", sNotification));
+    // unregister all
+    EXPECT_TRUE(IParent::registerForNotifications(instance, sNotification));
+    EXPECT_TRUE(manager->unregisterForNotifications("", "", sNotification));
 
-        // unregister all with instance name
-        EXPECT_TRUE(IParent::registerForNotifications(instance, sNotification));
-        EXPECT_TRUE(manager->unregisterForNotifications(IParent::descriptor,
-            "", sNotification));
+    // unregister all with instance name
+    EXPECT_TRUE(IParent::registerForNotifications(instance, sNotification));
+    EXPECT_TRUE(manager->unregisterForNotifications(IParent::descriptor, "", sNotification));
 
-        // unregister package listener
-        EXPECT_TRUE(IParent::registerForNotifications("", sNotification));
-        EXPECT_TRUE(manager->unregisterForNotifications(IParent::descriptor,
-            "", sNotification));
+    // unregister package listener
+    EXPECT_TRUE(IParent::registerForNotifications("", sNotification));
+    EXPECT_TRUE(manager->unregisterForNotifications(IParent::descriptor, "", sNotification));
 
-        // unregister listener for specific service and name
-        EXPECT_TRUE(IParent::registerForNotifications(instance, sNotification));
-        EXPECT_TRUE(manager->unregisterForNotifications(IParent::descriptor,
-            instance, sNotification));
+    // unregister listener for specific service and name
+    EXPECT_TRUE(IParent::registerForNotifications(instance, sNotification));
+    EXPECT_TRUE(manager->unregisterForNotifications(IParent::descriptor, instance, sNotification));
 
-        EXPECT_FALSE(manager->unregisterForNotifications("", "", sNotification));
+    EXPECT_FALSE(manager->unregisterForNotifications("", "", sNotification));
 
-        // TODO(b/32837397): remote destructor is lazy
-        // wp<ServiceNotification> wNotification = sNotification;
-        // sNotification = nullptr;
-        // EXPECT_EQ(nullptr, wNotification.promote().get());
-    }
+    // TODO(b/32837397): remote destructor is lazy
+    // wp<ServiceNotification> wNotification = sNotification;
+    // sNotification = nullptr;
+    // EXPECT_EQ(nullptr, wNotification.promote().get());
 }
 
-// passthrough TODO(b/31959402)
 TEST_F(HidlTest, ServiceAllNotificationTest) {
+    ServiceNotification* notification = new ServiceNotification();
+
+    std::string instanceOne = "test-instance-one";
+    std::string instanceTwo = "test-instance-two";
+    EXPECT_TRUE(ISimple::registerForNotifications("", notification));
+
+    Simple* instanceA = new Simple(1);
+    EXPECT_EQ(::android::OK, instanceA->registerAsService(instanceOne));
+    Simple* instanceB = new Simple(2);
+    EXPECT_EQ(::android::OK, instanceB->registerAsService(instanceTwo));
+
+    std::unique_lock<std::mutex> lock(notification->mutex);
+
+    notification->condition.wait_for(lock, std::chrono::milliseconds(2), [&notification]() {
+        return notification->getRegistrations().size() >= 2;
+    });
+
+    std::vector<std::string> registrations = notification->getRegistrations();
+    std::sort(registrations.begin(), registrations.end());
+
+    EXPECT_EQ(registrations.size(), 2u);
+
+    std::string descriptor = ISimple::descriptor;
+
+    EXPECT_EQ(
+        to_string(registrations.data(), registrations.size()),
+        "['" + descriptor + "/" + instanceOne + "', '" + descriptor + "/" + instanceTwo + "']");
+}
+
+TEST_F(HidlTest, InterfacesEqualTest) {
+    using android::hardware::interfacesEqual;
+
+    sp<IParent> service1 = IParent::getService("child", mode == PASSTHROUGH /* getStub */);
+    sp<IParent> service2 = service1;
+
+    // Passthrough services are reinstantiated whenever getService is called.
     if (mode == BINDERIZED) {
-        ServiceNotification *notification = new ServiceNotification();
-
-        std::string instanceOne = "test-instance-one";
-        std::string instanceTwo = "test-instance-two";
-        EXPECT_TRUE(ISimple::registerForNotifications("", notification));
-
-        Simple* instanceA = new Simple(1);
-        EXPECT_EQ(::android::OK, instanceA->registerAsService(instanceOne));
-        Simple* instanceB = new Simple(2);
-        EXPECT_EQ(::android::OK, instanceB->registerAsService(instanceTwo));
-
-        std::unique_lock<std::mutex> lock(notification->mutex);
-
-        notification->condition.wait_for(
-                lock,
-                std::chrono::milliseconds(2),
-                [&notification]() {
-                   return notification->getRegistrations().size() >= 2;
-                });
-
-        std::vector<std::string> registrations = notification->getRegistrations();
-        std::sort(registrations.begin(), registrations.end());
-
-        EXPECT_EQ(registrations.size(), 2u);
-
-        std::string descriptor = ISimple::descriptor;
-
-        EXPECT_EQ(to_string(registrations.data(), registrations.size()),
-                  "['" + descriptor + "/" + instanceOne + "', '"
-                       + descriptor + "/" + instanceTwo + "']");
+        service2 = IParent::getService("child");
     }
+
+    EXPECT_NE(nullptr, service1.get());
+    EXPECT_NE(nullptr, service2.get());
+    EXPECT_TRUE(interfacesEqual(service1, service2));
+
+    sp<IChild> child = IChild::castFrom(service1);
+    EXPECT_NE(nullptr, child.get());  // it is actually a child
+
+    EXPECT_TRUE(interfacesEqual(service1, child));
+    EXPECT_TRUE(interfacesEqual(service2, child));
 }
 
 TEST_F(HidlTest, TestToken) {
@@ -889,19 +901,57 @@
     }
 }
 
-inline uint64_t operator""_GB(unsigned long long num) {
-    return num * 1024 * 1024 * 1024;
-}
+TEST_F(HidlTest, MemoryBlock) {
+    const uint8_t kValue = 0xCA;
+    using ::android::hardware::IBinder;
+    using ::android::hardware::interfacesEqual;
+    using ::android::hardware::toBinder;
 
-TEST_F(HidlTest, FailedBatchSharedMemory) {
-    EXPECT_OK(ashmemAllocator->batchAllocate(1024, UINT64_MAX, [&](bool success, const auto& v) {
-        EXPECT_FALSE(success);
-        EXPECT_EQ(0u, v.size());
+    sp<HidlMemory> mem;
+    EXPECT_OK(ashmemAllocator->allocate(1024, [&](bool success, const hidl_memory& _mem) {
+        ASSERT_TRUE(success);
+        mem = HidlMemory::getInstance(_mem);
     }));
-    EXPECT_OK(ashmemAllocator->batchAllocate(1_GB, 1024, [&](bool success, const auto& v) {
-        EXPECT_FALSE(success);
-        EXPECT_EQ(0u, v.size());
+    memoryTest->set(*mem);
+    Return<sp<IMemoryToken>> tokenRet = memoryTest->get();
+    EXPECT_OK(tokenRet);
+    sp<IMemoryToken> token = tokenRet;
+    EXPECT_NE(nullptr, token.get());
+    EXPECT_OK(token->get([&](const hidl_memory& mem) {
+        sp<IMemory> memory = mapMemory(mem);
+
+        EXPECT_NE(nullptr, memory.get());
+
+        uint8_t* data = static_cast<uint8_t*>(static_cast<void*>(memory->getPointer()));
+        EXPECT_NE(data, nullptr);
+
+        EXPECT_EQ(memory->getSize(), mem.size());
+
+        memory->update();
+        memset(data, 0, memory->getSize());
+        memory->commit();
+
+        memoryTest->fillMemory(mem, kValue);
+        memory->commit();
     }));
+    MemoryBlock blk = {token, 0x200 /* size */, 0x100 /* offset */};
+    EXPECT_OK(memoryTest->haveSomeMemoryBlock(blk, [&](const MemoryBlock& blkBack) {
+        sp<IMemoryToken> tokenBack = blkBack.token;
+        EXPECT_TRUE(interfacesEqual(token, tokenBack));
+        EXPECT_EQ(blkBack.size, 0x200ULL);
+        EXPECT_EQ(blkBack.offset, 0x100ULL);
+        blk = blkBack;
+    }));
+
+    sp<IMemoryToken> mtoken = blk.token;
+    mtoken->get([&](const hidl_memory& mem) {
+        sp<IMemory> memory = mapMemory(mem);
+        uint8_t* data = static_cast<uint8_t*>(static_cast<void*>(memory->getPointer()));
+        EXPECT_NE(data, nullptr);
+        for (size_t i = 0; i < mem.size(); i++) {
+            EXPECT_EQ(kValue, data[i]);
+        }
+    });
 }
 
 TEST_F(HidlTest, NullSharedMemory) {
@@ -922,6 +972,23 @@
     }));
 }
 
+TEST_F(HidlTest, FooConvertToBoolIfSmallTest) {
+    hidl_vec<IFoo::Union> u = {
+        {.intValue = 7}, {.intValue = 0}, {.intValue = 1}, {.intValue = 8},
+    };
+    EXPECT_OK(foo->convertToBoolIfSmall(IFoo::Discriminator::INT, u, [&](const auto& res) {
+        ASSERT_EQ(4u, res.size());
+        EXPECT_EQ(IFoo::Discriminator::INT, res[0].discriminator);
+        EXPECT_EQ(u[0].intValue, res[0].value.intValue);
+        EXPECT_EQ(IFoo::Discriminator::BOOL, res[1].discriminator);
+        EXPECT_EQ(static_cast<bool>(u[1].intValue), res[1].value.boolValue);
+        EXPECT_EQ(IFoo::Discriminator::BOOL, res[2].discriminator);
+        EXPECT_EQ(static_cast<bool>(u[2].intValue), res[2].value.boolValue);
+        EXPECT_EQ(IFoo::Discriminator::INT, res[3].discriminator);
+        EXPECT_EQ(u[3].intValue, res[3].value.intValue);
+    }));
+}
+
 TEST_F(HidlTest, FooDoThisTest) {
     ALOGI("CLIENT call doThis.");
     EXPECT_OK(foo->doThis(1.0f));
@@ -1669,6 +1736,72 @@
     EXPECT_OK(bar->thisIsNew());
 }
 
+TEST_F(HidlTest, TrieSimpleTest) {
+    trieInterface->newTrie([&](const TrieNode& trie) {
+        trieInterface->addStrings(trie, {"a", "ba"}, [&](const TrieNode& trie) {
+            trieInterface->containsStrings(
+                trie, {"", "a", "b", "ab", "ba", "c"}, [](const hidl_vec<bool>& response) {
+                    EXPECT_EQ(response,
+                              std::vector<bool>({false, true, false, false, true, false}));
+                });
+
+            trieInterface->addStrings(trie, {"", "ab", "bab"}, [&](const TrieNode& trie) {
+                trieInterface->containsStrings(
+                    trie, {"", "a", "b", "ab", "ba", "c"}, [](const hidl_vec<bool>& response) {
+                        EXPECT_EQ(response,
+                                  std::vector<bool>({true, true, false, true, true, false}));
+                    });
+            });
+        });
+    });
+}
+
+struct RandomString {
+    std::string next() {
+        std::string ret(lengthDist(rng), 0);
+        std::generate(ret.begin(), ret.end(), [&]() { return charDist(rng); });
+        return ret;
+    }
+
+    RandomString() : rng(std::random_device{}()), lengthDist(5, 10), charDist('a', 'a' + 10) {}
+
+   private:
+    std::default_random_engine rng;
+    std::uniform_int_distribution<> lengthDist;
+    std::uniform_int_distribution<> charDist;
+};
+
+TEST_F(HidlTest, TrieStressTest) {
+    const size_t REQUEST_NUM = 1000;
+    RandomString stringGenerator;
+
+    trieInterface->newTrie([&](const TrieNode& trie) {
+        std::vector<std::string> strings(REQUEST_NUM);
+        for (auto& str : strings) {
+            str = stringGenerator.next();
+        }
+
+        trieInterface->addStrings(
+            trie, hidl_vec<hidl_string>(strings.begin(), strings.end()), [&](const TrieNode& trie) {
+                std::unordered_set<std::string> addedStrings(strings.begin(), strings.end());
+
+                for (size_t i = 0; i != REQUEST_NUM; ++i) {
+                    strings.push_back(stringGenerator.next());
+                }
+
+                std::vector<bool> trueResponse(strings.size());
+                std::transform(strings.begin(), strings.end(), trueResponse.begin(),
+                               [&](const std::string& str) {
+                                   return addedStrings.find(str) != addedStrings.end();
+                               });
+
+                trieInterface->containsStrings(
+                    trie, hidl_vec<hidl_string>(strings.begin(), strings.end()),
+                    [&](const hidl_vec<bool>& response) { EXPECT_EQ(response, trueResponse); });
+            });
+    });
+}
+
 class HidlMultithreadTest : public ::testing::Test {
    public:
     sp<IMultithread> multithreadInterface;
@@ -2010,9 +2143,11 @@
 #endif
 
 template <class T>
-static void waitForServer(const std::string &serviceName) {
-    ::android::hardware::details::waitForHwService(T::descriptor, serviceName);
-}
+struct WaitForServer {
+    static void run(const std::string& serviceName) {
+        ::android::hardware::details::waitForHwService(T::descriptor, serviceName);
+    }
+};
 
 int forkAndRunTests(TestMode mode, bool enableDelayMeasurementTests) {
     pid_t child;
@@ -2122,7 +2257,7 @@
         handleStatus(pStatus, "PASSTHROUGH");
     }
     if (b) {
-        EACH_SERVER(waitForServer);
+        runOnEachServer<WaitForServer>();
         ALOGI("BINDERIZED Test result = %d", bStatus);
         handleStatus(bStatus, "BINDERIZED ");
     }
diff --git a/test/hidl_test_helper b/test/hidl_test/hidl_test_helper
similarity index 100%
rename from test/hidl_test_helper
rename to test/hidl_test/hidl_test_helper
diff --git a/test/hidl_test/hidl_test_servers.cpp b/test/hidl_test/hidl_test_servers.cpp
new file mode 100644
index 0000000..2862002
--- /dev/null
+++ b/test/hidl_test/hidl_test_servers.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hidl_test_servers"
+
+#include "hidl_test.h"
+
+#include <android-base/logging.h>
+
+#include <android/hardware/tests/baz/1.0/IBaz.h>
+
+#include <hidl/LegacySupport.h>
+
+#include <hwbinder/IPCThreadState.h>
+
+#include <sys/wait.h>
+#include <signal.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+using ::android::hardware::tests::baz::V1_0::IBaz;
+
+using ::android::hardware::defaultPassthroughServiceImplementation;
+using ::android::hardware::IPCThreadState;
+
+static std::vector<std::pair<std::string, pid_t>> gPidList;
+
+void signal_handler_server(int signal) {
+    if (signal == SIGTERM) {
+        IPCThreadState::shutdown();
+        exit(0);
+    }
+}
+
+template <class T>
+struct ForkServer {
+    static void run(const std::string& serviceName) {
+        pid_t pid;
+
+        if ((pid = fork()) == 0) {
+            // in child process
+            signal(SIGTERM, signal_handler_server);
+            int status = defaultPassthroughServiceImplementation<T>(serviceName);
+            exit(status);
+        }
+
+        gPidList.push_back({serviceName, pid});
+    }
+};
+
+static void killServer(pid_t pid, const char *serverName) {
+    if (kill(pid, SIGTERM)) {
+        ALOGE("Could not kill %s; errno = %d", serverName, errno);
+    } else {
+        int status;
+        ALOGE("Waiting for %s to exit...", serverName);
+        waitpid(pid, &status, 0);
+        if (status != 0) {
+            ALOGE("%s terminates abnormally with status %d", serverName, status);
+        } else {
+            ALOGE("%s killed successfully", serverName);
+        }
+        ALOGE("Continuing...");
+    }
+}
+
+void signal_handler(int signal) {
+    if (signal == SIGTERM) {
+        for (auto p : gPidList) {
+            killServer(p.second, p.first.c_str());
+        }
+        exit(0);
+    }
+}
+
+int main(int /* argc */, char* /* argv */ []) {
+    setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+
+    runOnEachServer<ForkServer>();
+
+    ForkServer<IBaz>::run("dyingBaz");
+
+    signal(SIGTERM, signal_handler);
+    // Parent process should not exit before the forked child processes.
+    pause();
+
+    return 0;
+}
diff --git a/test/hidl_test/static_test.cpp b/test/hidl_test/static_test.cpp
new file mode 100644
index 0000000..3222f03
--- /dev/null
+++ b/test/hidl_test/static_test.cpp
@@ -0,0 +1,265 @@
+
+#include <android/hardware/tests/bar/1.0/IImportRules.h>
+#include <android/hardware/tests/expression/1.0/IExpression.h>
+#include <android/hardware/tests/foo/1.0/IFoo.h>
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_array;
+using ::android::hardware::tests::bar::V1_0::IImportRules;
+using ::android::hardware::tests::expression::V1_0::IExpression;
+using ::android::hardware::tests::foo::V1_0::IFoo;
+using ::android::sp;
+
+namespace {
+
+// Check array dimension order
+static_assert(std::is_same<IFoo::multidimArrayOne, hidl_array<int32_t, 5, 6, 7>>::value,
+              "hidl-gen output array dimention order is incorrect");
+static_assert(std::is_same<IFoo::multidimArrayTwo, hidl_array<int32_t, 8, 9, 10, 5, 6, 7>>::value,
+              "hidl-gen output array dimention order is incorrect");
+static_assert(
+    std::is_same<IFoo::multidimArrayThree, hidl_array<int32_t, 2, 3, 4, 8, 9, 10, 5, 6, 7>>::value,
+    "hidl-gen output array dimention order is incorrect");
+
+// Check correct type in ambiguous case
+static_assert(std::is_same<decltype(IFoo::S1::foo), IFoo::InnerTestStruct>::value,
+              "hidl-gen wrong (inner) type in output");
+static_assert(!std::is_same<decltype(IFoo::S1::foo), IFoo::S1::InnerTestStruct>::value,
+              "hidl-gen wrong (inner) type in output");
+static_assert((int32_t) decltype(IFoo::S2::foo)::VALUE == 0,
+              "hidl-gen wrong (inner) type in output");
+
+// Ensure (statically) that the types in IImportRules resolves to the correct types by
+// overriding the methods with fully namespaced types as arguments.
+struct MyImportRules : public IImportRules {
+    Return<void> rule0a(
+        const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
+        return Void();
+    }
+
+    Return<void> rule0a1(
+        const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
+        return Void();
+    }
+
+    Return<void> rule0b(
+        const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
+        return Void();
+    }
+
+    Return<void> rule0b1(
+        const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
+        return Void();
+    }
+
+    Return<void> rule0c(const ::android::hardware::tests::foo::V1_0::Outer&) override {
+        return Void();
+    }
+
+    Return<void> rule0d(const ::android::hardware::tests::foo::V1_0::Outer&) override {
+        return Void();
+    }
+
+    Return<void> rule0e(
+        const ::android::hardware::tests::bar::V1_0::IImportRules::Outer::Inner&) override {
+        return Void();
+    }
+
+    Return<void> rule0f(
+        const ::android::hardware::tests::bar::V1_0::IImportRules::Outer::Inner&) override {
+        return Void();
+    }
+
+    Return<void> rule0g(const ::android::hardware::tests::foo::V1_0::Outer::Inner&) override {
+        return Void();
+    }
+
+    Return<void> rule0h(const ::android::hardware::tests::foo::V1_0::Outer::Inner&) override {
+        return Void();
+    }
+
+    Return<void> rule1a(const ::android::hardware::tests::bar::V1_0::Def&) override {
+        return Void();
+    }
+
+    Return<void> rule1b(const ::android::hardware::tests::foo::V1_0::Def&) override {
+        return Void();
+    }
+
+    Return<void> rule2a(const ::android::hardware::tests::foo::V1_0::Unrelated&) override {
+        return Void();
+    }
+
+    Return<void> rule2b(const sp<::android::hardware::tests::foo::V1_0::IFooCallback>&) override {
+        return Void();
+    }
+};
+
+template <typename E>
+constexpr bool operator==(E actual, typename std::underlying_type<E>::type expected) {
+    return expected == static_cast<typename std::underlying_type<E>::type>(actual);
+}
+
+// See IExpression.hal
+// Use the C++ compiler to compute these expressions and make sure hidl-gen emits the same result.
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec1 == 0, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec2 == 1, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec3 == -1, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec4 == ~0, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec5 == 2147483647, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec6 == -2147483648, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec7 == 2147483648, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec8 == -2147483649, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec9 == ~(-1), "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixHex1 == 0x7fffffff, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixHex2 == 0x80000000, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixHex3 == 0xffffffff, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::longHex1 == 0xffffffffl, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::longHex2 == 0Xfffffffff, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::longHex3 == 0x7fffffffffffffff, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::longHex4 == 0x8000000000000000, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::longHex5 == 0xFFFFFFFFFFFFFFFF, "");
+
+static_assert(IExpression::SuffixedLiteralTypeGuessing::decInt32_1 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::decInt32_2 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::decInt64_1 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::decInt64_2 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::decInt64_3 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::decInt64_4 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::hexInt32_1 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::hexUInt32_1 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::hexUInt32_2 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::hexInt64_1 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::hexUInt64_1 == 1, "");
+
+static_assert(IExpression::Int64LiteralTypeGuessing::noSuffixDec11 == -2147483648,
+              "1 + 0x7fffffff");
+static_assert(IExpression::Int64LiteralTypeGuessing::noSuffixDec12 == 2147483647, "0x80000000 - 1");
+
+static_assert(IExpression::Int32BitShifting::int32BitShift1 == 1 << 31, "");
+static_assert(IExpression::UInt32BitShifting::uint32BitShift1 == 1 << 31, "");
+static_assert(IExpression::Int64BitShifting::int64BitShift1 == 1LL << 63, "1l << 63");
+static_assert(IExpression::UInt64BitShifting::uint64BitShift1 == 1LL << 63, "1l << 63");
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconstant-logical-operand"
+#pragma clang diagnostic ignored "-Wlogical-op-parentheses"
+#pragma clang diagnostic ignored "-Wbitwise-op-parentheses"
+
+static_assert(IExpression::Precedence::literal == (4), "");
+static_assert(IExpression::Precedence::neg == (-4), "");
+static_assert(IExpression::Precedence::literalL == (-4L), "");
+static_assert(IExpression::Precedence::hex == (0xffffffff), "");
+static_assert(IExpression::Precedence::hexLong == (0xffffffffl), "");
+static_assert(IExpression::Precedence::hexLong2 == (0xfffffffff), "");
+static_assert(IExpression::Precedence::simpleArithmetic == (4 + 1), "");
+static_assert(IExpression::Precedence::simpleArithmetic2 == (2 + 3 - 4), "");
+static_assert(IExpression::Precedence::simpleArithmetic3 == (2 - 3 + 4), "");
+static_assert(IExpression::Precedence::simpleBoolExpr == (1 == 4), "");
+static_assert(IExpression::Precedence::simpleLogical == (1 && 1), "");
+static_assert(IExpression::Precedence::simpleLogical2 == (1 || 1 && 0),
+              "&& higher than || in HIDL");
+static_assert(IExpression::Precedence::simpleComp == (1 < 2), "");
+static_assert(IExpression::Precedence::boolExpr1 == (!((3 != 4 || (2 < 3 <= 3 > 4)) >= 0)), "");
+static_assert(IExpression::Precedence::boolExpr == (1 == 7 && !((3 != 4 || (2 < 3 <= 3 > 4)) >= 0)),
+              "");
+static_assert(IExpression::Precedence::simpleBitShift == (1 << 2), "");
+static_assert(IExpression::Precedence::simpleBitShift2 == (4 >> 1), "");
+// Shifting negative number of bits is UB in HIDL, so the value is not tested.
+// static_assert(IExpression::Precedence::simpleBitShiftNeg == (4 << -1), "");
+static_assert(IExpression::Precedence::simpleArithmeticRightShift == (1 << 31 >> 31), "");
+static_assert(IExpression::Precedence::simpleBitExpr == (1 | 16 >> 2), "");
+static_assert(IExpression::Precedence::simpleBitExpr2 == (0x0f ^ 0x33 & 0x99),
+              "& higher than ^ in HIDL");
+static_assert(IExpression::Precedence::bitExpr == (~42 & (1 << 3 | 16 >> 2) ^ 7),
+              "& higher than ^ in HIDL");
+static_assert(IExpression::Precedence::arithmeticExpr == (2 + 3 - 4 * -7 / (10 % 3)), "");
+static_assert(IExpression::Precedence::messyExpr == (2 + (-3 & 4 / 7)), "");
+static_assert(IExpression::Precedence::paranExpr == ((((((1 + 1)))))), "");
+static_assert(IExpression::Precedence::ternary == (1 ? 2 : 3), "");
+static_assert(IExpression::Precedence::ternary2 == (1 && 2 ? 3 : 4), "");
+static_assert(IExpression::Precedence::complicatedTernary2 == (1 - 1 && 2 + 3 || 5 ? 7 * 8 : -3),
+              "&& higher than || in HIDL");
+
+#pragma clang diagnostic pop
+
+static_assert(IExpression::OperatorSanityCheck::plus == 1, "");
+static_assert(IExpression::OperatorSanityCheck::minus == 1, "");
+static_assert(IExpression::OperatorSanityCheck::product == 1, "");
+static_assert(IExpression::OperatorSanityCheck::division == 1, "");
+static_assert(IExpression::OperatorSanityCheck::mod == 1, "");
+static_assert(IExpression::OperatorSanityCheck::bit_or == 1, "");
+static_assert(IExpression::OperatorSanityCheck::bit_or2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::bit_and == 1, "");
+static_assert(IExpression::OperatorSanityCheck::bit_xor == 1, "");
+static_assert(IExpression::OperatorSanityCheck::lt1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::lt2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::gt1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::gt2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::gte1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::gte2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::lte1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::lte2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::ne1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::ne2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::lshift == 1, "");
+static_assert(IExpression::OperatorSanityCheck::rshift == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logor1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logor2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logor3 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logor4 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logand1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logand2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logand3 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logand4 == 1, "");
+
+static_assert(IExpression::Grayscale::WHITE == 126, "");
+static_assert(IExpression::Grayscale::GRAY == 127, "");
+static_assert(IExpression::Grayscale::DARK_GRAY == -128, "");
+static_assert(IExpression::Grayscale::BLACK == -127, "");
+
+static_assert(IExpression::Color::RED == -126, "");
+static_assert(IExpression::Color::RUBY == 0, "");
+static_assert(IExpression::Color::GREEN == 1, "");
+static_assert(IExpression::Color::BLUE == 5, "");
+static_assert(IExpression::Color::CYAN == 6, "");
+static_assert(IExpression::Color::ORANGE == 7, "");
+static_assert(IExpression::Color::ROSE == 126, "");
+
+static_assert(std::is_same<std::underlying_type<IExpression::Foo1>::type,
+                           std::underlying_type<IExpression::Foo2>::type>::value,
+              "");
+static_assert(std::is_same<std::underlying_type<IExpression::Foo1>::type,
+                           std::underlying_type<IExpression::Foo3>::type>::value,
+              "");
+static_assert(std::is_same<std::underlying_type<IExpression::Foo1>::type,
+                           std::underlying_type<IExpression::Foo4>::type>::value,
+              "");
+static_assert(IExpression::Foo3::BAR1 == 0, "");
+static_assert(IExpression::Foo3::BAR2 == 10, "");
+static_assert(IExpression::Foo4::BAR3 == 11, "");
+static_assert(IExpression::Foo4::BAR4 == 21, "");
+
+static_assert(IExpression::Number::MAX == 255, "");
+static_assert(IExpression::Number::MAX_PLUS_1 == 0, "");
+static_assert(IExpression::Number::MAX_PLUS_2 == 1, "");
+
+static_assert(IExpression::Constants::CONST_FOO == 0, "");
+static_assert(IExpression::Constants::CONST_BAR == 70, "");
+static_assert(IExpression::Constants::MAX_ARRAY_SIZE == 20, "");
+static_assert(IExpression::Constants::MAX_ARRAY_SIZE2 == 21, "");
+static_assert(IExpression::Constants::MAX_ARRAY_SIZE3 == 40, "");
+static_assert(IExpression::Constants::MY_INT32_MAX_MINUS_1 == 0x7FFFFFFE, "");
+static_assert(IExpression::Constants::MY_INT32_MAX == 0x7FFFFFFF, "");
+static_assert(IExpression::Constants::MY_INT32_MIN == 0x80000000, "");
+static_assert(IExpression::Constants::MY_INT32_MIN_PLUS_1 == 0x80000001, "");
+
+// Ensure (statically) that the array sizes in function arguments are correct.
+struct Expression : public IExpression {
+    Return<void> foo1(const hidl_array<int32_t, 1>&) override { return Void(); }
+    Return<void> foo2(const hidl_array<int32_t, 13>&) override { return Void(); }
+    Return<void> foo3(const hidl_array<int32_t, 20>&) override { return Void(); }
+};
+
+}  // anonymous namespace
diff --git a/test/hidl_test_servers.cpp b/test/hidl_test_servers.cpp
deleted file mode 100644
index cd14d30..0000000
--- a/test/hidl_test_servers.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "hidl_test_servers"
-
-#include "hidl_test.h"
-
-#include <android-base/logging.h>
-
-#include <android/hardware/tests/bar/1.0/IBar.h>
-#include <android/hardware/tests/baz/1.0/IBaz.h>
-#include <android/hardware/tests/foo/1.0/BnHwSimple.h>
-#include <android/hardware/tests/foo/1.0/BpHwSimple.h>
-#include <android/hardware/tests/foo/1.0/BsSimple.h>
-#include <android/hardware/tests/hash/1.0/IHash.h>
-#include <android/hardware/tests/inheritance/1.0/IChild.h>
-#include <android/hardware/tests/inheritance/1.0/IFetcher.h>
-#include <android/hardware/tests/inheritance/1.0/IParent.h>
-#include <android/hardware/tests/memory/1.0/IMemoryTest.h>
-#include <android/hardware/tests/multithread/1.0/IMultithread.h>
-#include <android/hardware/tests/pointer/1.0/IGraph.h>
-#include <android/hardware/tests/pointer/1.0/IPointer.h>
-
-#include <hidl/LegacySupport.h>
-
-#include <hwbinder/IPCThreadState.h>
-
-#include <sys/wait.h>
-#include <signal.h>
-
-#include <string>
-#include <utility>
-#include <vector>
-
-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;
-using ::android::hardware::tests::pointer::V1_0::IGraph;
-using ::android::hardware::tests::pointer::V1_0::IPointer;
-using ::android::hardware::tests::memory::V1_0::IMemoryTest;
-using ::android::hardware::tests::multithread::V1_0::IMultithread;
-
-using ::android::hardware::defaultPassthroughServiceImplementation;
-using ::android::hardware::IPCThreadState;
-
-static std::vector<std::pair<std::string, pid_t>> gPidList;
-
-void signal_handler_server(int signal) {
-    if (signal == SIGTERM) {
-        IPCThreadState::shutdown();
-        exit(0);
-    }
-}
-
-template <class T>
-static void forkServer(const std::string &serviceName) {
-    pid_t pid;
-
-    if ((pid = fork()) == 0) {
-        // in child process
-        signal(SIGTERM, signal_handler_server);
-        int status = defaultPassthroughServiceImplementation<T>(serviceName);
-        exit(status);
-    }
-
-    gPidList.push_back({serviceName, pid});
-    return;
-}
-
-static void killServer(pid_t pid, const char *serverName) {
-    if (kill(pid, SIGTERM)) {
-        ALOGE("Could not kill %s; errno = %d", serverName, errno);
-    } else {
-        int status;
-        ALOGE("Waiting for %s to exit...", serverName);
-        waitpid(pid, &status, 0);
-        if (status != 0) {
-            ALOGE("%s terminates abnormally with status %d", serverName, status);
-        } else {
-            ALOGE("%s killed successfully", serverName);
-        }
-        ALOGE("Continuing...");
-    }
-}
-
-void signal_handler(int signal) {
-    if (signal == SIGTERM) {
-        for (auto p : gPidList) {
-            killServer(p.second, p.first.c_str());
-        }
-        exit(0);
-    }
-}
-
-int main(int /* argc */, char* /* argv */ []) {
-    setenv("TREBLE_TESTING_OVERRIDE", "true", true);
-
-    EACH_SERVER(forkServer);
-
-    forkServer<IBaz>("dyingBaz");
-
-    signal(SIGTERM, signal_handler);
-    // Parent process should not exit before the forked child processes.
-    pause();
-
-    return 0;
-}
diff --git a/test/host_test/Android.bp b/test/host_test/Android.bp
new file mode 100644
index 0000000..d5e6511
--- /dev/null
+++ b/test/host_test/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test_host {
+    name: "hidl-gen-host_test",
+    defaults: ["hidl-gen-defaults"],
+
+    shared_libs: [
+        "libhidl-gen",
+        "libhidl-gen-ast",
+        "libhidl-gen-utils",
+    ],
+
+    srcs: ["main.cpp"],
+}
\ No newline at end of file
diff --git a/test/host_test/main.cpp b/test/host_test/main.cpp
new file mode 100644
index 0000000..9377306
--- /dev/null
+++ b/test/host_test/main.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libhidl-gen-utils"
+
+#include <gtest/gtest.h>
+
+#include <ConstantExpression.h>
+#include <Coordinator.h>
+#include <hidl-util/FQName.h>
+
+#define EXPECT_EQ_OK(expectResult, call, ...)        \
+    do {                                             \
+        std::string result;                          \
+        status_t err = (call)(__VA_ARGS__, &result); \
+        EXPECT_EQ(err, ::android::OK);               \
+        EXPECT_EQ(expectResult, result);             \
+    } while (false)
+
+namespace android {
+
+class HidlGenHostTest : public ::testing::Test {};
+
+TEST_F(HidlGenHostTest, CoordinatorTest) {
+    Coordinator coordinator;
+
+    std::string error;
+    EXPECT_EQ(OK, coordinator.addPackagePath("a.b", "a1/b1", &error));
+    EXPECT_TRUE(error.empty());
+    EXPECT_NE(OK, coordinator.addPackagePath("a.b", "a2/b2/", &error));
+    EXPECT_FALSE(error.empty());
+
+    coordinator.addDefaultPackagePath("a.b", "a3/b3/"); // should take path above
+    coordinator.addDefaultPackagePath("a.c", "a4/b4/"); // should succeed
+
+    EXPECT_EQ_OK("a.b", coordinator.getPackageRoot, FQName("a.b.foo@1.0"));
+    EXPECT_EQ_OK("a.c", coordinator.getPackageRoot, FQName("a.c.foo.bar@1.0::IFoo"));
+
+    // getPackagePath(fqname, relative, sanitized, ...)
+    EXPECT_EQ_OK("a1/b1/foo/1.0/", coordinator.getPackagePath, FQName("a.b.foo@1.0"), false, false);
+    EXPECT_EQ_OK("a4/b4/foo/bar/1.0/", coordinator.getPackagePath, FQName("a.c.foo.bar@1.0::IFoo"),
+                 false, false);
+    EXPECT_EQ_OK("a1/b1/foo/V1_0/", coordinator.getPackagePath, FQName("a.b.foo@1.0"), false, true);
+    EXPECT_EQ_OK("a4/b4/foo/bar/V1_0/", coordinator.getPackagePath, FQName("a.c.foo.bar@1.0::IFoo"),
+                 false, true);
+    EXPECT_EQ_OK("foo/1.0/", coordinator.getPackagePath, FQName("a.b.foo@1.0"), true, false);
+    EXPECT_EQ_OK("foo/bar/1.0/", coordinator.getPackagePath, FQName("a.c.foo.bar@1.0::IFoo"), true,
+                 false);
+    EXPECT_EQ_OK("foo/V1_0/", coordinator.getPackagePath, FQName("a.b.foo@1.0"), true, true);
+    EXPECT_EQ_OK("foo/bar/V1_0/", coordinator.getPackagePath, FQName("a.c.foo.bar@1.0::IFoo"), true,
+                 true);
+}
+
+TEST_F(HidlGenHostTest, CoordinatorFilepathTest) {
+    using Location = Coordinator::Location;
+
+    Coordinator coordinator;
+    coordinator.setOutputPath("foo/");
+    coordinator.setRootPath("bar/");
+
+    std::string error;
+    EXPECT_EQ(OK, coordinator.addPackagePath("a.b", "a1/b1", &error));
+    EXPECT_TRUE(error.empty());
+
+    const static FQName kName = FQName("a.b.c@1.2");
+
+    // get file names
+    EXPECT_EQ_OK("foo/x.y", coordinator.getFilepath, kName, Location::DIRECT, "x.y");
+    EXPECT_EQ_OK("foo/a1/b1/c/1.2/x.y", coordinator.getFilepath, kName, Location::PACKAGE_ROOT,
+                 "x.y");
+    EXPECT_EQ_OK("foo/a/b/c/1.2/x.y", coordinator.getFilepath, kName, Location::GEN_OUTPUT, "x.y");
+    EXPECT_EQ_OK("foo/a/b/c/V1_2/x.y", coordinator.getFilepath, kName, Location::GEN_SANITIZED,
+                 "x.y");
+
+    // get directories
+    EXPECT_EQ_OK("foo/", coordinator.getFilepath, kName, Location::DIRECT, "");
+    EXPECT_EQ_OK("foo/a1/b1/c/1.2/", coordinator.getFilepath, kName, Location::PACKAGE_ROOT, "");
+    EXPECT_EQ_OK("foo/a/b/c/1.2/", coordinator.getFilepath, kName, Location::GEN_OUTPUT, "");
+    EXPECT_EQ_OK("foo/a/b/c/V1_2/", coordinator.getFilepath, kName, Location::GEN_SANITIZED, "");
+}
+
+TEST_F(HidlGenHostTest, LocationTest) {
+    Location a{{"file", 3, 4}, {"file", 3, 5}};
+    Location b{{"file", 3, 6}, {"file", 3, 7}};
+    Location c{{"file", 4, 4}, {"file", 4, 5}};
+
+    Location other{{"other", 0, 0}, {"other", 0, 1}};
+
+    EXPECT_LT(a, b);
+    EXPECT_LT(b, c);
+    EXPECT_LT(a, c);
+    EXPECT_FALSE(Location::inSameFile(a, other));
+}
+
+int main(int argc, char **argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
+
+}  // namespace android
\ No newline at end of file
diff --git a/test/impl_test/Android.bp b/test/impl_test/Android.bp
index b220f24..a440eb9 100644
--- a/test/impl_test/Android.bp
+++ b/test/impl_test/Android.bp
@@ -42,4 +42,9 @@
         "libutils",
         "android.hardware.tests.foo@1.0",
     ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
 }
diff --git a/test/java_test/Android.bp b/test/java_test/Android.bp
index 23bdf08..f7b731d 100644
--- a/test/java_test/Android.bp
+++ b/test/java_test/Android.bp
@@ -2,6 +2,8 @@
     name: "hidl_test_java_native",
     srcs: ["hidl_test_java_native.cpp"],
 
+    cflags: ["-Wall", "-Werror"],
+
     shared_libs: [
         "libbase",
         "libhidlbase",
diff --git a/test/java_test/Android.mk b/test/java_test/Android.mk
index 1f2e868..a41fa5d 100644
--- a/test/java_test/Android.mk
+++ b/test/java_test/Android.mk
@@ -7,6 +7,7 @@
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
 LOCAL_JAVA_LIBRARIES :=                          \
     android.hidl.base-V1.0-java                  \
+    android.hidl.manager-V1.0-java               \
     android.hardware.tests.baz-V1.0-java         \
     android.hardware.tests.expression-V1.0-java  \
     android.hardware.tests.inheritance-V1.0-java
@@ -20,13 +21,15 @@
 LOCAL_MODULE_CLASS := NATIVE_TESTS
 LOCAL_SRC_FILES := hidl_test_java
 
-LOCAL_ADDITIONAL_DEPENDENCIES :=                \
-    hidl_test_java_lib                          \
-    hidl_test_java_native                       \
-
 LOCAL_REQUIRED_MODULES :=                       \
     hidl_test_java_lib                          \
+    hidl_test_java_native                       \
     android.hidl.base-V1.0-java                 \
+    android.hidl.manager-V1.0-java              \
     android.hardware.tests.baz-V1.0-java
 
+LOCAL_REQUIRED_MODULES_arm64 := hidl_test_java_native_32
+LOCAL_REQUIRED_MODULES_x86_64 := hidl_test_java_native_32
+LOCAL_REQUIRED_MODULES_mips64 := hidl_test_java_native_32
+
 include $(BUILD_PREBUILT)
diff --git a/test/java_test/hidl_test_java b/test/java_test/hidl_test_java
index 2076d00..1cdfb3f 100644
--- a/test/java_test/hidl_test_java
+++ b/test/java_test/hidl_test_java
@@ -22,7 +22,6 @@
         # Test Java server with native client
         app_process $base/bin com.android.commands.hidl_test_java.HidlTestJava -s &
         NATIVE_PID=$!
-        sleep 1
         $native -c \
             && echo "native client => Java server PASSED" \
             || (echo "native client => Java server FAILED" && false) || e=1
@@ -38,4 +37,4 @@
 echo "Summary: $e"
 [ $e -eq 0 ] && echo "All tests PASSED." || echo "Test(s) FAILED."
 
-exit $e
\ No newline at end of file
+exit $e
diff --git a/test/java_test/hidl_test_java_native.cpp b/test/java_test/hidl_test_java_native.cpp
index 0d3a7c3..d155e09 100644
--- a/test/java_test/hidl_test_java_native.cpp
+++ b/test/java_test/hidl_test_java_native.cpp
@@ -22,7 +22,7 @@
 #include <android/hardware/tests/baz/1.0/IBaz.h>
 
 #include <hidl/LegacySupport.h>
-
+#include <hidl/ServiceManagement.h>
 #include <gtest/gtest.h>
 
 #include <hidl/HidlTransportSupport.h>
@@ -77,9 +77,13 @@
     void SetUp() override {
         using namespace ::android::hardware;
 
+        ::android::hardware::details::waitForHwService(
+                IBaz::descriptor, "baz");
+
         baz = IBaz::getService("baz");
 
         CHECK(baz != NULL);
+        CHECK(baz->isRemote());
     }
 
     void TearDown() override {
@@ -489,6 +493,87 @@
                 }));
 }
 
+TEST_F(HidlTest, BazTestArrays) {
+    IBase::LotsOfPrimitiveArrays in;
+
+    for (size_t i = 0; i < 128; ++i) {
+        in.byte1[i] = i;
+        in.boolean1[i] = (i & 4) != 0;
+        in.double1[i] = i;
+    }
+
+    size_t k = 0;
+    for (size_t i = 0; i < 8; ++i) {
+        for (size_t j = 0; j < 128; ++j, ++k) {
+            in.byte2[i][j] = k;
+            in.boolean2[i][j] = (k & 4) != 0;
+            in.double2[i][j] = k;
+        }
+    }
+
+    size_t m = 0;
+    for (size_t i = 0; i < 8; ++i) {
+        for (size_t j = 0; j < 16; ++j) {
+            for (size_t k = 0; k < 128; ++k, ++m) {
+                in.byte3[i][j][k] = m;
+                in.boolean3[i][j][k] = (m & 4) != 0;
+                in.double3[i][j][k] = m;
+            }
+        }
+    }
+
+    EXPECT_OK(
+            baz->testArrays(in,
+                [&](const auto &out) {
+                    EXPECT_EQ(in, out);
+                }));
+}
+
+TEST_F(HidlTest, BazTestByteVecs) {
+    hidl_vec<IBase::ByteOneDim> in;
+    in.resize(8);
+
+    size_t k = 0;
+    for (size_t i = 0; i < in.size(); ++i) {
+        for (size_t j = 0; j < 128; ++j, ++k) {
+            in[i][j] = k;
+        }
+    }
+
+    EXPECT_OK(baz->testByteVecs(
+                in, [&](const auto &out) { EXPECT_EQ(in, out); }));
+}
+
+TEST_F(HidlTest, BazTestBooleanVecs) {
+    hidl_vec<IBase::BooleanOneDim> in;
+    in.resize(8);
+
+    size_t k = 0;
+    for (size_t i = 0; i < in.size(); ++i) {
+        for (size_t j = 0; j < 128; ++j, ++k) {
+            in[i][j] = (k & 4) != 0;
+        }
+    }
+
+    EXPECT_OK(baz->testBooleanVecs(
+                in, [&](const auto &out) { EXPECT_EQ(in, out); }));
+}
+
+TEST_F(HidlTest, BazTestDoubleVecs) {
+    hidl_vec<IBase::DoubleOneDim> in;
+    in.resize(8);
+
+    size_t k = 0;
+    for (size_t i = 0; i < in.size(); ++i) {
+        for (size_t j = 0; j < 128; ++j, ++k) {
+            in[i][j] = k;
+        }
+    }
+
+    EXPECT_OK(baz->testDoubleVecs(
+                in, [&](const auto &out) { EXPECT_EQ(in, out); }));
+}
+
 int main(int argc, char **argv) {
     setenv("TREBLE_TESTING_OVERRIDE", "true", true);
 
diff --git a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
index 16dc793..f670732 100644
--- a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
+++ b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
@@ -16,6 +16,7 @@
 
 package com.android.commands.hidl_test_java;
 
+import android.hidl.manager.V1_0.IServiceManager;
 import android.hardware.tests.baz.V1_0.IBase;
 import android.hardware.tests.baz.V1_0.IBaz;
 import android.hardware.tests.baz.V1_0.IQuux;
@@ -29,6 +30,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 
 public final class HidlTestJava {
     private static final String TAG = "HidlTestJava";
@@ -115,6 +117,20 @@
         throw new RuntimeException();
     }
 
+    // .equals and HidlSupport.interfacesEqual should have the same behavior.
+    private void ExpectEqual(android.hidl.base.V1_0.IBase l, android.hidl.base.V1_0.IBase r) {
+        ExpectTrue(Objects.equals(l, r));
+        ExpectTrue(Objects.equals(r, l));
+        ExpectTrue(HidlSupport.interfacesEqual(l, r));
+        ExpectTrue(HidlSupport.interfacesEqual(r, l));
+    }
+    private void ExpectNotEqual(android.hidl.base.V1_0.IBase l, android.hidl.base.V1_0.IBase r) {
+        ExpectFalse(Objects.equals(l, r));
+        ExpectFalse(Objects.equals(r, l));
+        ExpectFalse(HidlSupport.interfacesEqual(l, r));
+        ExpectFalse(HidlSupport.interfacesEqual(r, l));
+    }
+
     class BazCallback extends IBazCallback.Stub {
         private boolean mCalled;
 
@@ -135,6 +151,12 @@
         public void hey() {
             mCalled = true;
         }
+
+        @Override public boolean equals(Object other) {
+            return other != null && other.getClass() == BazCallback.class &&
+                ((BazCallback) other).mCalled == mCalled;
+        }
+        @Override public int hashCode() { return mCalled ? 1 : 0; }
     }
 
     private String numberToEnglish(int x) {
@@ -240,6 +262,8 @@
             // Test proper exceptions are thrown
             try {
                 IBase proxy = IBase.getService("this-doesn't-exist");
+                // this should never run
+                ExpectTrue(false);
             } catch (Exception e) {
                 ExpectTrue(e instanceof NoSuchElementException);
             }
@@ -259,7 +283,18 @@
             ExpectTrue(quuxProxy == null);
         }
 
+        {
+            // Test waiting API
+            IBase baseProxyA = IBaz.getService("baz", true /* retry */);
+            ExpectTrue(baseProxyA != null);
+            IBase baseProxyB = IBaz.getService("baz", false /* retry */);
+            ExpectTrue(baseProxyB != null);
+        }
+
         IBaz proxy = IBaz.getService("baz");
+
+        proxy.ping();
+
         proxy.someBaseMethod();
 
         {
@@ -630,6 +665,142 @@
             }
         }
 
+        {
+            // TestArrays
+            IBase.LotsOfPrimitiveArrays in = new IBase.LotsOfPrimitiveArrays();
+
+            for (int i = 0; i < 128; ++i) {
+                in.byte1[i] = (byte)i;
+                in.boolean1[i] = (i & 4) != 0;
+                in.double1[i] = i;
+            }
+
+            int m = 0;
+            for (int i = 0; i < 8; ++i) {
+                for (int j = 0; j < 128; ++j, ++m) {
+                    in.byte2[i][j] = (byte)m;
+                    in.boolean2[i][j] = (m & 4) != 0;
+                    in.double2[i][j] = m;
+                }
+            }
+
+            m = 0;
+            for (int i = 0; i < 8; ++i) {
+                for (int j = 0; j < 16; ++j) {
+                    for (int k = 0; k < 128; ++k, ++m) {
+                        in.byte3[i][j][k] = (byte)m;
+                        in.boolean3[i][j][k] = (m & 4) != 0;
+                        in.double3[i][j][k] = m;
+                    }
+                }
+            }
+
+            IBase.LotsOfPrimitiveArrays out = proxy.testArrays(in);
+            ExpectTrue(in.equals(out));
+        }
+
+        {
+            // testByteVecs
+
+            ArrayList<byte[]> in = new ArrayList<byte[]>();
+
+            int k = 0;
+            for (int i = 0; i < in.size(); ++i) {
+                byte[] elem = new byte[128];
+                for (int j = 0; j < 128; ++j, ++k) {
+                    elem[j] = (byte)k;
+                }
+                in.add(elem);
+            }
+
+            ArrayList<byte[]> out = proxy.testByteVecs(in);
+            ExpectTrue(in.equals(out));
+        }
+
+        {
+            // testBooleanVecs
+
+            ArrayList<boolean[]> in = new ArrayList<boolean[]>();
+
+            int k = 0;
+            for (int i = 0; i < in.size(); ++i) {
+                boolean[] elem = new boolean[128];
+                for (int j = 0; j < 128; ++j, ++k) {
+                    elem[j] = (k & 4) != 0;
+                }
+                in.add(elem);
+            }
+
+            ArrayList<boolean[]> out = proxy.testBooleanVecs(in);
+            ExpectTrue(in.equals(out));
+        }
+
+        {
+            // testDoubleVecs
+
+            ArrayList<double[]> in = new ArrayList<double[]>();
+
+            int k = 0;
+            for (int i = 0; i < in.size(); ++i) {
+                double[] elem = new double[128];
+                for (int j = 0; j < 128; ++j, ++k) {
+                    elem[j] = k;
+                }
+                in.add(elem);
+            }
+
+            ArrayList<double[]> out = proxy.testDoubleVecs(in);
+            ExpectTrue(in.equals(out));
+        }
+
+        {
+            // testProxyEquals
+            // TODO(b/68727931): test passthrough services as well.
+
+            IBase proxy1 = IBase.getService("baz");
+            IBase proxy2 = IBase.getService("baz");
+            IBaz proxy3 = IBaz.getService("baz");
+            IBazCallback callback1 = new BazCallback();
+            IBazCallback callback2 = new BazCallback();
+            IServiceManager manager = IServiceManager.getService();
+
+            // test hwbinder proxies
+            ExpectEqual(proxy1, proxy2); // same proxy class
+            ExpectEqual(proxy1, proxy3); // different proxy class
+
+            // negative tests
+            ExpectNotEqual(proxy1, null);
+            ExpectNotEqual(proxy1, callback1); // proxy != stub
+            ExpectNotEqual(proxy1, manager);
+
+            // HidlSupport.interfacesEqual use overridden .equals for stubs
+            ExpectEqual(callback1, callback1);
+            ExpectEqual(callback1, callback2);
+            callback1.hey();
+            ExpectNotEqual(callback1, callback2);
+            callback2.hey();
+            ExpectEqual(callback1, callback2);
+
+            // test hash for proxies
+            java.util.HashSet<IBase> set = new java.util.HashSet<>();
+            set.add(proxy1);
+            ExpectTrue(set.contains(proxy1)); // hash is stable
+            ExpectTrue(set.contains(proxy2));
+            ExpectFalse(set.contains(manager));
+        }
+        {
+            IBaz baz = IBaz.getService("baz");
+            ExpectTrue(baz != null);
+            IBaz.StructWithInterface swi = new IBaz.StructWithInterface();
+            swi.dummy = baz;
+            swi.number = 12345678;
+            IBaz.StructWithInterface swi_back = baz.haveSomeStructWithInterface(swi);
+            ExpectTrue(swi_back != null);
+            ExpectTrue(swi_back.dummy != null);
+            ExpectTrue(HidlSupport.interfacesEqual(baz, swi_back.dummy));
+            ExpectTrue(swi_back.number == 12345678);
+        }
+
         // --- DEATH RECIPIENT TESTING ---
         // This must always be done last, since it will kill the native server process
         HidlDeathRecipient recipient1 = new HidlDeathRecipient();
@@ -817,6 +988,22 @@
                     (byte)(second.value & bf), (byte)((bf | bf) & third));
         }
 
+        public LotsOfPrimitiveArrays testArrays(LotsOfPrimitiveArrays in) {
+            return in;
+        }
+
+        public ArrayList<byte[]> testByteVecs(ArrayList<byte[]> in) {
+            return in;
+        }
+
+        public ArrayList<boolean[]> testBooleanVecs(ArrayList<boolean[]> in) {
+            return in;
+        }
+
+        public ArrayList<double[]> testDoubleVecs(ArrayList<double[]> in) {
+            return in;
+        }
+
         public byte returnABitField() {
             return 0;
         }
@@ -894,6 +1081,10 @@
         public void returnABunchOfStrings(returnABunchOfStringsCallback cb) {
             cb.onValues("Eins", "Zwei", "Drei");
         }
+
+        public StructWithInterface haveSomeStructWithInterface(StructWithInterface swi) {
+            return swi;
+        }
     }
 
     private void server() throws RemoteException {
diff --git a/test/run_all_device_tests.sh b/test/run_all_device_tests.sh
new file mode 100755
index 0000000..a32558a
--- /dev/null
+++ b/test/run_all_device_tests.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+function run() {
+    local FAILED_TESTS=()
+
+    # Tests directly relevant to HIDL infrustructure but aren't
+    # located in system/tools/hidl
+    local RELATED_RUNTIME_TESTS=(\
+        libhidl_test \
+    )
+
+    local RUN_TIME_TESTS=(\
+        libhidl-gen-utils_test \
+    )
+    RUN_TIME_TESTS+=(${RELATED_RUNTIME_TESTS[@]})
+
+    local SCRIPT_TESTS=(\
+        hidl_test\
+        hidl_test_java\
+        fmq_test\
+    )
+
+    $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode -j \
+        ${RUN_TIME_TESTS[*]} ${SCRIPT_TESTS[*]} || return
+
+    adb sync || return
+
+    local BITNESS=("nativetest" "nativetest64")
+
+    for test in ${RUN_TIME_TESTS[@]}; do
+        for bits in ${BITNESS[@]}; do
+            echo $bits $test
+            adb shell /data/$bits/$test/$test ||
+                FAILED_TESTS+=("$bits:$test")
+        done
+    done
+
+    for test in ${SCRIPT_TESTS[@]}; do
+        echo $test
+        adb shell /data/nativetest64/$test ||
+            FAILED_TESTS+=("$test")
+    done
+
+    echo
+    echo ===== ALL DEVICE TESTS SUMMARY =====
+    echo
+    if [ ${#FAILED_TESTS[@]} -gt 0 ]; then
+        for failed in ${FAILED_TESTS[@]}; do
+            echo "FAILED TEST: $failed"
+        done
+    else
+        echo "SUCCESS"
+    fi
+}
+
+run
\ No newline at end of file
diff --git a/test/run_all_host_tests.sh b/test/run_all_host_tests.sh
new file mode 100755
index 0000000..442efd7
--- /dev/null
+++ b/test/run_all_host_tests.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+function run() {
+    local FAILED_TESTS=()
+
+    local COMPILE_TIME_TESTS=(\
+        hidl_error_test \
+        hidl_export_test \
+        hidl_hash_test \
+        hidl_impl_test \
+        android.hardware.tests.foo@1.0-vts.driver \
+        android.hardware.tests.foo@1.0-vts.profiler)
+
+    local RUN_TIME_TESTS=(\
+        libhidl-gen-utils_test \
+        hidl-gen-host_test \
+    )
+
+    $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode -j \
+        ${COMPILE_TIME_TESTS[*]} ${RUN_TIME_TESTS[*]} || return
+
+    local BITNESS=("nativetest" "nativetest64")
+
+    for bits in ${BITNESS[@]}; do
+        for test in ${RUN_TIME_TESTS[@]}; do
+            echo $bits $test
+            $ANDROID_BUILD_TOP/out/host/linux-x86/$bits/$test/$test ||
+                FAILED_TESTS+=("$bits:$test")
+        done
+    done
+
+    echo
+    echo ===== ALL HOST TESTS SUMMARY =====
+    echo
+    if [ ${#FAILED_TESTS[@]} -gt 0 ]; then
+        for failed in ${FAILED_TESTS[@]}; do
+            echo "FAILED TEST: $failed"
+        done
+    else
+        echo "SUCCESS"
+    fi
+}
+
+run
\ No newline at end of file
diff --git a/test/utils_test/Android.bp b/test/utils_test/Android.bp
new file mode 100644
index 0000000..8637a17
--- /dev/null
+++ b/test/utils_test/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+    name: "libhidl-gen-utils_test",
+    defaults: ["hidl-gen-defaults"],
+    host_supported: true,
+
+    shared_libs: [
+        "libhidl-gen-utils",
+    ],
+
+    srcs: ["main.cpp"],
+}
\ No newline at end of file
diff --git a/test/utils_test/main.cpp b/test/utils_test/main.cpp
new file mode 100644
index 0000000..b2c8576
--- /dev/null
+++ b/test/utils_test/main.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libhidl-gen-utils"
+
+#include <hidl-util/FqInstance.h>
+#include <hidl-util/StringHelper.h>
+
+#include <gtest/gtest.h>
+#include <vector>
+
+using ::android::FqInstance;
+using ::android::StringHelper;
+
+class LibHidlGenUtilsTest : public ::testing::Test {};
+
+TEST_F(LibHidlGenUtilsTest, EndsWithTest) {
+    EXPECT_TRUE(StringHelper::EndsWith("", ""));
+    EXPECT_TRUE(StringHelper::EndsWith("a", ""));
+    EXPECT_TRUE(StringHelper::EndsWith("b", "b"));
+    EXPECT_TRUE(StringHelper::EndsWith("ab", "b"));
+    EXPECT_TRUE(StringHelper::EndsWith("ab", "ab"));
+    EXPECT_TRUE(StringHelper::EndsWith("abcd", "bcd"));
+    EXPECT_TRUE(StringHelper::EndsWith("abcd", "abcd"));
+    EXPECT_TRUE(StringHelper::EndsWith("abcdefghijk", "ijk"));
+    EXPECT_TRUE(StringHelper::EndsWith("abcdefghijk", "bcdefghijk"));
+
+    EXPECT_FALSE(StringHelper::EndsWith("", "a"));
+    EXPECT_FALSE(StringHelper::EndsWith("b", "a"));
+    EXPECT_FALSE(StringHelper::EndsWith("abcd", "ab"));
+}
+
+TEST_F(LibHidlGenUtilsTest, StartsWithTest) {
+    EXPECT_TRUE(StringHelper::StartsWith("", ""));
+    EXPECT_TRUE(StringHelper::StartsWith("a", ""));
+    EXPECT_TRUE(StringHelper::StartsWith("b", "b"));
+    EXPECT_TRUE(StringHelper::StartsWith("ab", "a"));
+    EXPECT_TRUE(StringHelper::StartsWith("ab", "ab"));
+    EXPECT_TRUE(StringHelper::StartsWith("abcd", "abc"));
+    EXPECT_TRUE(StringHelper::StartsWith("abcd", "abcd"));
+    EXPECT_TRUE(StringHelper::StartsWith("abcdefghijk", "abc"));
+    EXPECT_TRUE(StringHelper::StartsWith("abcdefghijk", "abcdefghij"));
+
+    EXPECT_FALSE(StringHelper::StartsWith("", "a"));
+    EXPECT_FALSE(StringHelper::StartsWith("b", "a"));
+    EXPECT_FALSE(StringHelper::StartsWith("abcd", "cd"));
+}
+
+TEST_F(LibHidlGenUtilsTest, Trim) {
+    EXPECT_EQ("", StringHelper::LTrim("", ""));
+    EXPECT_EQ("", StringHelper::LTrim("", "a"));
+    EXPECT_EQ("", StringHelper::LTrim("a", "a"));
+    EXPECT_EQ("a", StringHelper::LTrim("a", ""));
+    EXPECT_EQ("a", StringHelper::LTrim("a", "b"));
+    EXPECT_EQ("a", StringHelper::LTrim("ba", "b"));
+    EXPECT_EQ("f", StringHelper::LTrim("abcdef", "abcde"));
+    EXPECT_EQ("cdef", StringHelper::LTrim("abcdef", "ab"));
+    EXPECT_EQ("abcdef", StringHelper::LTrim("abcdef", ""));
+
+    EXPECT_EQ("", StringHelper::RTrim("", ""));
+    EXPECT_EQ("", StringHelper::RTrim("", "a"));
+    EXPECT_EQ("", StringHelper::RTrim("a", "a"));
+    EXPECT_EQ("a", StringHelper::RTrim("a", ""));
+    EXPECT_EQ("a", StringHelper::RTrim("a", "b"));
+    EXPECT_EQ("a", StringHelper::RTrim("ab", "b"));
+    EXPECT_EQ("a", StringHelper::RTrim("abcdef", "bcdef"));
+    EXPECT_EQ("abcd", StringHelper::RTrim("abcdef", "ef"));
+    EXPECT_EQ("abcdef", StringHelper::RTrim("abcdef", ""));
+}
+
+TEST_F(LibHidlGenUtilsTest, TrimAll) {
+    EXPECT_EQ("", StringHelper::LTrimAll("", ""));
+    EXPECT_EQ("", StringHelper::LTrimAll("", "a"));
+    EXPECT_EQ("", StringHelper::LTrimAll("", "ab"));
+    EXPECT_EQ("", StringHelper::LTrimAll("a", "a"));
+    EXPECT_EQ("", StringHelper::LTrimAll("aa", "a"));
+    EXPECT_EQ("b", StringHelper::LTrimAll("b", "a"));
+    EXPECT_EQ("b", StringHelper::LTrimAll("aaab", "a"));
+    EXPECT_EQ("c", StringHelper::LTrimAll("ababc", "ab"));
+    EXPECT_EQ("ac", StringHelper::LTrimAll("abac", "ab"));
+
+    EXPECT_EQ("", StringHelper::RTrimAll("", ""));
+    EXPECT_EQ("", StringHelper::RTrimAll("", "a"));
+    EXPECT_EQ("", StringHelper::RTrimAll("", "ab"));
+    EXPECT_EQ("", StringHelper::RTrimAll("a", "a"));
+    EXPECT_EQ("", StringHelper::RTrimAll("aa", "a"));
+    EXPECT_EQ("b", StringHelper::RTrimAll("b", "a"));
+    EXPECT_EQ("b", StringHelper::RTrimAll("baaa", "a"));
+    EXPECT_EQ("c", StringHelper::RTrimAll("cabab", "ab"));
+    EXPECT_EQ("ca", StringHelper::RTrimAll("caba", "ba"));
+}
+
+TEST_F(LibHidlGenUtilsTest, SplitString) {
+    std::vector<std::string> components;
+
+    StringHelper::SplitString("", '.', &components);
+    EXPECT_EQ(std::vector<std::string>({""}), components);
+    StringHelper::SplitString("a.", '.', &components);
+    EXPECT_EQ(std::vector<std::string>({"a", ""}), components);
+    StringHelper::SplitString(".a", '.', &components);
+    EXPECT_EQ(std::vector<std::string>({"", "a"}), components);
+    StringHelper::SplitString("..", '.', &components);
+    EXPECT_EQ(std::vector<std::string>({"", "", ""}), components);
+    StringHelper::SplitString("asdf.asdf", '.', &components);
+    EXPECT_EQ(std::vector<std::string>({"asdf", "asdf"}), components);
+}
+
+TEST_F(LibHidlGenUtilsTest, JoinStrings) {
+    EXPECT_EQ("", StringHelper::JoinStrings({}, ""));
+    EXPECT_EQ("", StringHelper::JoinStrings({}, "a"));
+    EXPECT_EQ("a", StringHelper::JoinStrings({"a"}, ""));
+    EXPECT_EQ("a,b", StringHelper::JoinStrings({"a", "b"}, ","));
+    EXPECT_EQ("ab,", StringHelper::JoinStrings({"ab", ""}, ","));
+    EXPECT_EQ(",ab", StringHelper::JoinStrings({"", "ab"}, ","));
+    EXPECT_EQ("a.,b", StringHelper::JoinStrings({"a", "b"}, ".,"));
+    EXPECT_EQ("a,b,c", StringHelper::JoinStrings({"a", "b", "c"}, ","));
+    EXPECT_EQ("abc.,def.,ghi", StringHelper::JoinStrings({"abc", "def", "ghi"}, ".,"));
+}
+
+TEST_F(LibHidlGenUtilsTest, FqInstance1) {
+    FqInstance e;
+    ASSERT_TRUE(e.setTo("android.hardware.foo@1.0::IFoo/instance"));
+    EXPECT_EQ("android.hardware.foo@1.0::IFoo/instance", e.string());
+    ASSERT_TRUE(e.hasPackage());
+    EXPECT_EQ("android.hardware.foo", e.getPackage());
+    ASSERT_TRUE(e.hasVersion());
+    EXPECT_EQ(1u, e.getMajorVersion());
+    EXPECT_EQ(0u, e.getMinorVersion());
+    EXPECT_EQ((std::make_pair<size_t, size_t>(1u, 0u)), e.getVersion());
+    ASSERT_TRUE(e.hasInterface());
+    EXPECT_EQ("IFoo", e.getInterface());
+    ASSERT_TRUE(e.hasInstance());
+    EXPECT_EQ("instance", e.getInstance());
+}
+
+TEST_F(LibHidlGenUtilsTest, FqInstance2) {
+    FqInstance e;
+    ASSERT_TRUE(e.setTo("@1.0::IFoo/instance"));
+    EXPECT_EQ("@1.0::IFoo/instance", e.string());
+    ASSERT_FALSE(e.hasPackage());
+    ASSERT_TRUE(e.hasVersion());
+    EXPECT_EQ((std::make_pair<size_t, size_t>(1u, 0u)), e.getVersion());
+    ASSERT_TRUE(e.hasInterface());
+    EXPECT_EQ("IFoo", e.getInterface());
+    ASSERT_TRUE(e.hasInstance());
+    EXPECT_EQ("instance", e.getInstance());
+}
+
+TEST_F(LibHidlGenUtilsTest, FqInstance3) {
+    FqInstance e;
+    ASSERT_TRUE(e.setTo("IFoo/instance"));
+    EXPECT_EQ("IFoo/instance", e.string());
+    ASSERT_FALSE(e.hasPackage());
+    ASSERT_FALSE(e.hasVersion());
+    ASSERT_TRUE(e.hasInterface());
+    EXPECT_EQ("IFoo", e.getInterface());
+    ASSERT_TRUE(e.hasInstance());
+    EXPECT_EQ("instance", e.getInstance());
+}
+
+TEST_F(LibHidlGenUtilsTest, FqInstanceFqNameOnly) {
+    FqInstance e;
+    for (auto testString :
+         {"android.hardware.foo@1.0::IFoo.Type", "@1.0::IFoo.Type", "android.hardware.foo@1.0",
+          "IFoo.Type", "Type", "android.hardware.foo@1.0::IFoo.Type:MY_ENUM_VALUE",
+          "@1.0::IFoo.Type:MY_ENUM_VALUE", "IFoo.Type:MY_ENUM_VALUE"}) {
+        ASSERT_TRUE(e.setTo(testString));
+        EXPECT_EQ(testString, e.string());
+        ASSERT_FALSE(e.hasInstance());
+    };
+}
+
+TEST_F(LibHidlGenUtilsTest, FqInstanceIdentifier) {
+    FqInstance e;
+    ASSERT_TRUE(e.setTo("Type"));
+    EXPECT_EQ("Type", e.string());
+    ASSERT_FALSE(e.hasInstance());
+}
+
+int main(int argc, char **argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/test/vendor/1.0/Android.bp b/test/vendor/1.0/Android.bp
index f640216..8519fa5 100644
--- a/test/vendor/1.0/Android.bp
+++ b/test/vendor/1.0/Android.bp
@@ -1,90 +1,23 @@
-// This file is autogenerated by hidl-gen. Do not edit manually.
+// This file is autogenerated by hidl-gen -Landroidbp.
 
-filegroup {
-    name: "tests.vendor@1.0_hal",
+hidl_interface {
+    name: "hidl.tests.vendor@1.0",
+    owner: "some-owner-name",
+    root: "hidl.tests",
     srcs: [
         "types.hal",
         "IVendor.hal",
     ],
+    interfaces: [
+        "android.hardware.tests.baz@1.0",
+        "android.hidl.base@1.0",
+    ],
+    types: [
+        "Bar",
+        "Foo",
+        "FooToo",
+    ],
+    gen_java: true,
+    gen_java_constants: true,
 }
 
-genrule {
-    name: "tests.vendor@1.0_genc++",
-    tools: ["hidl-gen"],
-    cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport -rtests:system/tools/hidl/test/ tests.vendor@1.0",
-    srcs: [
-        ":tests.vendor@1.0_hal",
-    ],
-    out: [
-        "tests/vendor/1.0/types.cpp",
-        "tests/vendor/1.0/VendorAll.cpp",
-    ],
-}
-
-genrule {
-    name: "tests.vendor@1.0_genc++_headers",
-    tools: ["hidl-gen"],
-    cmd: "$(location hidl-gen) -o $(genDir) -Lc++-headers -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport -rtests:system/tools/hidl/test/ tests.vendor@1.0",
-    srcs: [
-        ":tests.vendor@1.0_hal",
-    ],
-    out: [
-        "tests/vendor/1.0/types.h",
-        "tests/vendor/1.0/hwtypes.h",
-        "tests/vendor/1.0/IVendor.h",
-        "tests/vendor/1.0/IHwVendor.h",
-        "tests/vendor/1.0/BnHwVendor.h",
-        "tests/vendor/1.0/BpHwVendor.h",
-        "tests/vendor/1.0/BsVendor.h",
-    ],
-}
-
-cc_library {
-    name: "tests.vendor@1.0",
-    defaults: ["hidl-module-defaults"],
-    generated_sources: ["tests.vendor@1.0_genc++"],
-    generated_headers: ["tests.vendor@1.0_genc++_headers"],
-    export_generated_headers: ["tests.vendor@1.0_genc++_headers"],
-    vendor_available: true,
-    shared_libs: [
-        "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
-        "liblog",
-        "libutils",
-        "libcutils",
-        "android.hardware.tests.baz@1.0",
-    ],
-    export_shared_lib_headers: [
-        "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
-        "libutils",
-        "android.hardware.tests.baz@1.0",
-    ],
-}
-
-cc_library {
-    name: "tests.vendor@1.0_vendor",
-    defaults: ["hidl-module-defaults"],
-    generated_sources: ["tests.vendor@1.0_genc++"],
-    generated_headers: ["tests.vendor@1.0_genc++_headers"],
-    export_generated_headers: ["tests.vendor@1.0_genc++_headers"],
-    vendor: true,
-    shared_libs: [
-        "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
-        "liblog",
-        "libutils",
-        "libcutils",
-        "android.hardware.tests.baz@1.0",
-    ],
-    export_shared_lib_headers: [
-        "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
-        "libutils",
-        "android.hardware.tests.baz@1.0",
-    ],
-}
diff --git a/test/vendor/1.0/Android.mk b/test/vendor/1.0/Android.mk
deleted file mode 100644
index 5caa041..0000000
--- a/test/vendor/1.0/Android.mk
+++ /dev/null
@@ -1,235 +0,0 @@
-# This file is autogenerated by hidl-gen. Do not edit manually.
-
-LOCAL_PATH := $(call my-dir)
-
-################################################################################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := tests.vendor-V1.0-java
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-
-intermediates := $(call local-generated-sources-dir, COMMON)
-
-HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)
-
-LOCAL_JAVA_LIBRARIES := \
-    android.hardware.tests.baz-V1.0-java \
-    android.hidl.base-V1.0-java \
-
-
-#
-# Build types.hal (Bar)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/Bar.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
-        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
-        -Ljava \
-        -randroid.hardware:hardware/interfaces \
-        -randroid.hidl:system/libhidl/transport \
-        -rtests:system/tools/hidl/test/ \
-        tests.vendor@1.0::types.Bar
-
-$(GEN): $(LOCAL_PATH)/types.hal
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build types.hal (Foo)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/Foo.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
-        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
-        -Ljava \
-        -randroid.hardware:hardware/interfaces \
-        -randroid.hidl:system/libhidl/transport \
-        -rtests:system/tools/hidl/test/ \
-        tests.vendor@1.0::types.Foo
-
-$(GEN): $(LOCAL_PATH)/types.hal
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build types.hal (FooToo)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/FooToo.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
-        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
-        -Ljava \
-        -randroid.hardware:hardware/interfaces \
-        -randroid.hidl:system/libhidl/transport \
-        -rtests:system/tools/hidl/test/ \
-        tests.vendor@1.0::types.FooToo
-
-$(GEN): $(LOCAL_PATH)/types.hal
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build IVendor.hal
-#
-GEN := $(intermediates)/tests/vendor/V1_0/IVendor.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IVendor.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
-        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
-        -Ljava \
-        -randroid.hardware:hardware/interfaces \
-        -randroid.hidl:system/libhidl/transport \
-        -rtests:system/tools/hidl/test/ \
-        tests.vendor@1.0::IVendor
-
-$(GEN): $(LOCAL_PATH)/IVendor.hal
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-include $(BUILD_JAVA_LIBRARY)
-
-
-################################################################################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := tests.vendor-V1.0-java-static
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-
-intermediates := $(call local-generated-sources-dir, COMMON)
-
-HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android.hardware.tests.baz-V1.0-java-static \
-    android.hidl.base-V1.0-java-static \
-
-
-#
-# Build types.hal (Bar)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/Bar.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
-        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
-        -Ljava \
-        -randroid.hardware:hardware/interfaces \
-        -randroid.hidl:system/libhidl/transport \
-        -rtests:system/tools/hidl/test/ \
-        tests.vendor@1.0::types.Bar
-
-$(GEN): $(LOCAL_PATH)/types.hal
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build types.hal (Foo)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/Foo.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
-        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
-        -Ljava \
-        -randroid.hardware:hardware/interfaces \
-        -randroid.hidl:system/libhidl/transport \
-        -rtests:system/tools/hidl/test/ \
-        tests.vendor@1.0::types.Foo
-
-$(GEN): $(LOCAL_PATH)/types.hal
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build types.hal (FooToo)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/FooToo.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
-        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
-        -Ljava \
-        -randroid.hardware:hardware/interfaces \
-        -randroid.hidl:system/libhidl/transport \
-        -rtests:system/tools/hidl/test/ \
-        tests.vendor@1.0::types.FooToo
-
-$(GEN): $(LOCAL_PATH)/types.hal
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build IVendor.hal
-#
-GEN := $(intermediates)/tests/vendor/V1_0/IVendor.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IVendor.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
-        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
-        -Ljava \
-        -randroid.hardware:hardware/interfaces \
-        -randroid.hidl:system/libhidl/transport \
-        -rtests:system/tools/hidl/test/ \
-        tests.vendor@1.0::IVendor
-
-$(GEN): $(LOCAL_PATH)/IVendor.hal
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-
-################################################################################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := tests.vendor-V1.0-java-constants
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-
-intermediates := $(call local-generated-sources-dir, COMMON)
-
-HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/Constants.java
-$(GEN): $(HIDL)
-$(GEN): $(LOCAL_PATH)/types.hal
-$(GEN): $(LOCAL_PATH)/IVendor.hal
-
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
-        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
-        -Ljava-constants \
-        -randroid.hardware:hardware/interfaces \
-        -randroid.hidl:system/libhidl/transport \
-        -rtests:system/tools/hidl/test/ \
-        tests.vendor@1.0
-
-$(GEN):
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-# Avoid dependency cycle of framework.jar -> this-library -> framework.jar
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/test/vendor/1.0/IVendor.hal b/test/vendor/1.0/IVendor.hal
index 3beebea..c963d3f 100644
--- a/test/vendor/1.0/IVendor.hal
+++ b/test/vendor/1.0/IVendor.hal
@@ -1,37 +1,39 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package tests.vendor@1.0;
-
-import android.hardware.tests.baz@1.0;
-
-interface IVendor extends IBaz {
-
-    struct StructTest {
-        vec<uint8_t> b;
-        string d;
-        uint8_t[4][4] e;
-    };
-
-    fun2(vec<uint8_t> a) generates(vec<uint8_t> b);
-    fun4(string a) generates(string b);
-    fun5(uint8_t[4][4] a) generates(uint8_t[4][4] b);
-
-    // Testing static functions in derived class
-    // overriding virtual functions in parent class
-    // due to namespace resolution.
-    registerForNotifications();
-};
\ No newline at end of file
+// note this file tests carriage returns

+

+/*

+ * Copyright (C) 2016 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package hidl.tests.vendor@1.0;

+

+import android.hardware.tests.baz@1.0;

+

+interface IVendor extends IBaz {

+

+    struct StructTest {

+        vec<uint8_t> b;

+        string d;

+        uint8_t[4][4] e;

+    };

+

+    fun2(vec<uint8_t> a) generates(vec<uint8_t> b);

+    fun4(string a) generates(string b);

+    fun5(uint8_t[4][4] a) generates(uint8_t[4][4] b);

+

+    // Testing static functions in derived class

+    // overriding virtual functions in parent class

+    // due to namespace resolution.

+    registerForNotifications();

+};

diff --git a/test/vendor/1.0/types.hal b/test/vendor/1.0/types.hal
index 511389e..4763f0e 100644
--- a/test/vendor/1.0/types.hal
+++ b/test/vendor/1.0/types.hal
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package tests.vendor@1.0;
+package hidl.tests.vendor@1.0;
 
 enum Bar : uint32_t {
     A
diff --git a/test/vendor/1.0/update-base.sh b/test/vendor/1.0/update-base.sh
index 7940328..933480f 100755
--- a/test/vendor/1.0/update-base.sh
+++ b/test/vendor/1.0/update-base.sh
@@ -4,7 +4,7 @@
 # It should typically be created elsewhere.
 
 options="-Lexport-header \
-         -r tests:system/tools/hidl/test/\
+         -r hidl.tests:system/tools/hidl/test/\
          -r android.hidl:system/libhidl/transport\
          -r android.hardware:hardware/interfaces"
 
diff --git a/test/vendor/1.1/Android.bp b/test/vendor/1.1/Android.bp
index 6d41300..80cecb2 100644
--- a/test/vendor/1.1/Android.bp
+++ b/test/vendor/1.1/Android.bp
@@ -1,90 +1,18 @@
-// This file is autogenerated by hidl-gen. Do not edit manually.
+// This file is autogenerated by hidl-gen -Landroidbp.
 
-filegroup {
-    name: "tests.vendor@1.1_hal",
+hidl_interface {
+    name: "hidl.tests.vendor@1.1",
+    owner: "some-owner-name",
+    root: "hidl.tests",
     srcs: [
         "IVendor.hal",
     ],
+    interfaces: [
+        "android.frameworks.displayservice@1.0",
+        "android.hardware.tests.baz@1.0",
+        "android.hidl.base@1.0",
+        "hidl.tests.vendor@1.0",
+    ],
+    gen_java: true,
 }
 
-genrule {
-    name: "tests.vendor@1.1_genc++",
-    tools: ["hidl-gen"],
-    cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport -rtests:system/tools/hidl/test/ tests.vendor@1.1",
-    srcs: [
-        ":tests.vendor@1.1_hal",
-    ],
-    out: [
-        "tests/vendor/1.1/VendorAll.cpp",
-    ],
-}
-
-genrule {
-    name: "tests.vendor@1.1_genc++_headers",
-    tools: ["hidl-gen"],
-    cmd: "$(location hidl-gen) -o $(genDir) -Lc++-headers -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport -rtests:system/tools/hidl/test/ tests.vendor@1.1",
-    srcs: [
-        ":tests.vendor@1.1_hal",
-    ],
-    out: [
-        "tests/vendor/1.1/IVendor.h",
-        "tests/vendor/1.1/IHwVendor.h",
-        "tests/vendor/1.1/BnHwVendor.h",
-        "tests/vendor/1.1/BpHwVendor.h",
-        "tests/vendor/1.1/BsVendor.h",
-    ],
-}
-
-cc_library {
-    name: "tests.vendor@1.1",
-    defaults: ["hidl-module-defaults"],
-    generated_sources: ["tests.vendor@1.1_genc++"],
-    generated_headers: ["tests.vendor@1.1_genc++_headers"],
-    export_generated_headers: ["tests.vendor@1.1_genc++_headers"],
-    vendor_available: true,
-    shared_libs: [
-        "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
-        "liblog",
-        "libutils",
-        "libcutils",
-        "android.hardware.tests.baz@1.0",
-        "tests.vendor@1.0",
-    ],
-    export_shared_lib_headers: [
-        "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
-        "libutils",
-        "android.hardware.tests.baz@1.0",
-        "tests.vendor@1.0",
-    ],
-}
-
-cc_library {
-    name: "tests.vendor@1.1_vendor",
-    defaults: ["hidl-module-defaults"],
-    generated_sources: ["tests.vendor@1.1_genc++"],
-    generated_headers: ["tests.vendor@1.1_genc++_headers"],
-    export_generated_headers: ["tests.vendor@1.1_genc++_headers"],
-    vendor: true,
-    shared_libs: [
-        "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
-        "liblog",
-        "libutils",
-        "libcutils",
-        "android.hardware.tests.baz@1.0",
-        "tests.vendor@1.0_vendor",
-    ],
-    export_shared_lib_headers: [
-        "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
-        "libutils",
-        "android.hardware.tests.baz@1.0",
-        "tests.vendor@1.0_vendor",
-    ],
-}
diff --git a/test/vendor/1.1/Android.mk b/test/vendor/1.1/Android.mk
deleted file mode 100644
index 4427516..0000000
--- a/test/vendor/1.1/Android.mk
+++ /dev/null
@@ -1,82 +0,0 @@
-# This file is autogenerated by hidl-gen. Do not edit manually.
-
-LOCAL_PATH := $(call my-dir)
-
-################################################################################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := tests.vendor-V1.1-java
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-
-intermediates := $(call local-generated-sources-dir, COMMON)
-
-HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)
-
-LOCAL_JAVA_LIBRARIES := \
-    android.hardware.tests.baz-V1.0-java \
-    android.hidl.base-V1.0-java \
-    tests.vendor-V1.0-java \
-
-
-#
-# Build IVendor.hal
-#
-GEN := $(intermediates)/tests/vendor/V1_1/IVendor.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IVendor.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
-        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
-        -Ljava \
-        -randroid.hardware:hardware/interfaces \
-        -randroid.hidl:system/libhidl/transport \
-        -rtests:system/tools/hidl/test/ \
-        tests.vendor@1.1::IVendor
-
-$(GEN): $(LOCAL_PATH)/IVendor.hal
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-include $(BUILD_JAVA_LIBRARY)
-
-
-################################################################################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := tests.vendor-V1.1-java-static
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-
-intermediates := $(call local-generated-sources-dir, COMMON)
-
-HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android.hardware.tests.baz-V1.0-java-static \
-    android.hidl.base-V1.0-java-static \
-    tests.vendor-V1.0-java-static \
-
-
-#
-# Build IVendor.hal
-#
-GEN := $(intermediates)/tests/vendor/V1_1/IVendor.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IVendor.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
-        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
-        -Ljava \
-        -randroid.hardware:hardware/interfaces \
-        -randroid.hidl:system/libhidl/transport \
-        -rtests:system/tools/hidl/test/ \
-        tests.vendor@1.1::IVendor
-
-$(GEN): $(LOCAL_PATH)/IVendor.hal
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/test/vendor/1.1/IVendor.hal b/test/vendor/1.1/IVendor.hal
index 4821048..437d79f 100644
--- a/test/vendor/1.1/IVendor.hal
+++ b/test/vendor/1.1/IVendor.hal
@@ -14,10 +14,14 @@
  * limitations under the License.
  */
 
-package tests.vendor@1.1;
+package hidl.tests.vendor@1.1;
 
-import tests.vendor@1.0::IVendor;
+import hidl.tests.vendor@1.0::IVendor;
 
-interface IVendor extends IVendor {
+// this import is unused, but it should still show up in the
+// associated Android.bp
+import android.frameworks.displayservice@1.0;
+
+interface IVendor extends @1.0::IVendor {
     doAdditionalFunctionality();
 };
diff --git a/test/vendor/Android.bp b/test/vendor/Android.bp
new file mode 100644
index 0000000..dfcd349
--- /dev/null
+++ b/test/vendor/Android.bp
@@ -0,0 +1,4 @@
+hidl_package_root {
+    name: "hidl.tests",
+    path: "system/tools/hidl/test",
+}
diff --git a/test/vendor/Android.mk b/test/vendor/Android.mk
deleted file mode 100644
index 3bda3b9..0000000
--- a/test/vendor/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-LOCAL_PATH:= $(call my-dir)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/test/vendor/update-makefile.sh b/test/vendor/update-makefile.sh
index 87e7340..49ced20 100755
--- a/test/vendor/update-makefile.sh
+++ b/test/vendor/update-makefile.sh
@@ -1,10 +1,9 @@
 #!/bin/bash
 
-options="-r tests:system/tools/hidl/test/ \
-         -r android.hidl:system/libhidl/transport \
+options="-O some-owner-name
+         -r hidl.tests:system/tools/hidl/test/
+         -r android.hidl:system/libhidl/transport
          -r android.hardware:hardware/interfaces"
 
-hidl-gen -Lmakefile $options tests.vendor@1.0;
-hidl-gen -Landroidbp $options -t tests.vendor@1.0;
-hidl-gen -Lmakefile $options tests.vendor@1.1;
-hidl-gen -Landroidbp $options -t tests.vendor@1.1;
+hidl-gen -Landroidbp $options hidl.tests.vendor@1.0;
+hidl-gen -Landroidbp $options hidl.tests.vendor@1.1;
diff --git a/test/version_test/Android.bp b/test/version_test/Android.bp
index 696f501..3629b8a 100644
--- a/test/version_test/Android.bp
+++ b/test/version_test/Android.bp
@@ -10,6 +10,8 @@
          "    test.version.version@2.3" +
          "    test.version.version@2.4" +
          "    test.version.version@2.5" +
+         "    test.version.version@3.0" +
+         "    test.version.version@3.1" +
          "&&" +
          "($(location hidl-gen) -L check " +
          "    -r test.version:system/tools/hidl/test/version_test/bad1" +
@@ -27,6 +29,10 @@
          "    -r test.version:system/tools/hidl/test/version_test/bad4" +
          "    test.version.version@2.3 2>&1 | grep \"doesn't pass minor version\" > /dev/null)" +
          "&&" +
+         "($(location hidl-gen) -L check " +
+         "    -r test.version:system/tools/hidl/test/version_test/bad5" +
+         "    test.version.version@2.3 2>&1 | grep \"doesn't pass minor version\" > /dev/null)" +
+         "&&" +
          "echo 'int main(){return 0;}' > $(genDir)/TODO_b_37575883.cpp",
     out: ["TODO_b_37575883.cpp"],
 
@@ -48,6 +54,8 @@
         "bad4/version/2.2/IBar.hal",
         "bad4/version/2.2/IFoo.hal",
         "bad4/version/2.3/IBaz.hal",
+        "bad5/version/2.2/IFoo.hal",
+        "bad5/version/2.3/IBaz.hal",
         "good/version/1.0/IFoo.hal",
         "good/version/2.2/IBar.hal",
         "good/version/2.2/IFoo.hal",
@@ -57,10 +65,14 @@
         "good/version/2.4/IFoo.hal",
         "good/version/2.5/IBar.hal",
         "good/version/2.5/IFoo.hal",
+        "good/version/3.0/types.hal",
+        "good/version/3.1/ICanExtendTypesOnly.hal",
+        "good/version/3.1/types.hal",
     ]
 }
 
 cc_test_host {
     name: "hidl_version_test",
+    cflags: ["-Wall", "-Werror"],
     generated_sources: ["hidl_hash_version_gen"],
-}
\ No newline at end of file
+}
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/version_test/bad5/version/2.2/IFoo.hal
similarity index 86%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/version_test/bad5/version/2.2/IFoo.hal
index d79e6ab..a672f76 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/version_test/bad5/version/2.2/IFoo.hal
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
+
+package test.version.version@2.2;
 
 interface IFoo {
-};
\ No newline at end of file
+
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/version_test/bad5/version/2.3/IBaz.hal
similarity index 83%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/version_test/bad5/version/2.3/IBaz.hal
index d79e6ab..8e71cd2 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/version_test/bad5/version/2.3/IBaz.hal
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+package test.version.version@2.3;
+
+interface IBaz {
+
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/version_test/good/version/3.0/types.hal
similarity index 90%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/version_test/good/version/3.0/types.hal
index d79e6ab..27db609 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/version_test/good/version/3.0/types.hal
@@ -13,7 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+package test.version.version@3.0;
+
+enum A : uint32_t {
+    B
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/version_test/good/version/3.1/ICanExtendTypesOnly.hal
similarity index 81%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/version_test/good/version/3.1/ICanExtendTypesOnly.hal
index d79e6ab..cbbfa8e 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/version_test/good/version/3.1/ICanExtendTypesOnly.hal
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+package test.version.version@3.1;
+
+interface ICanExtendTypesOnly {};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/version_test/good/version/3.1/types.hal
similarity index 88%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/version_test/good/version/3.1/types.hal
index d79e6ab..bc9964d 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/version_test/good/version/3.1/types.hal
@@ -13,7 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package test.error.error@1.0;
 
-interface IFoo {
-};
\ No newline at end of file
+package test.version.version@3.1;
+
+import @3.0::A;
+
+enum A : @3.0::A {
+    C
+};
diff --git a/update-all-google-makefiles.sh b/update-all-google-makefiles.sh
index 7491ac6..178137d 100755
--- a/update-all-google-makefiles.sh
+++ b/update-all-google-makefiles.sh
@@ -7,4 +7,4 @@
 $ANDROID_BUILD_TOP/frameworks/hardware/interfaces/update-makefiles.sh
 $ANDROID_BUILD_TOP/system/hardware/interfaces/update-makefiles.sh
 
-$ANDROID_BUILD_TOP/system/tools/hidl/test/vendor/1.0/update-makefile.sh
+$ANDROID_BUILD_TOP/system/tools/hidl/test/vendor/update-makefile.sh
diff --git a/update-makefiles-helper.sh b/update-makefiles-helper.sh
index 904c4ed..30f38a6 100755
--- a/update-makefiles-helper.sh
+++ b/update-makefiles-helper.sh
@@ -52,19 +52,6 @@
 }
 
 ##
-# Subdirectories of a directory which contain Android.bps
-# Note, does not return Android.bp in the current directory.
-#
-# Usage: get_bp_dirs dir
-function get_bp_dirs() {
-  find $1/*                          \
-    -mindepth 1                      \
-    -name "Android.bp"               \
-    -printf "%H\n"                   \
-    | sort | uniq
-}
-
-##
 # Returns directory path for a package
 # Usage: get_package_dir package_root_dir package_prefix package
 function get_package_dir() {
@@ -77,10 +64,16 @@
 # Helps manage the package root of a HAL directory.
 # Should be called from the android root directory.
 #
-# Usage: do_makefiles_update [package:root ...]
+# Usage: do_makefiles_update [-O owner-name] [package:root ...]
 # Where the first package root is the current one.
 #
 function do_makefiles_update() {
+  local owner=
+  if [[ "$1" = "-O" ]]; then
+      owner="$2"
+      shift 2
+  fi
+
   local root_or_cwd=${ANDROID_BUILD_TOP%%/}${ANDROID_BUILD_TOP:+/}
 
   local current_package=$(package_root_to_package $1)
@@ -95,32 +88,7 @@
 
   for p in $packages; do
     echo "Updating $p";
-    local additional_options=
-    if [[ -f $(get_package_dir $current_dir $current_package $p)/.hidl_for_test ]]; then additional_options="-t"; fi
-    hidl-gen -Lmakefile $root_arguments $p;
+    hidl-gen -O "$owner" -Landroidbp $root_arguments $p;
     rc=$?; if [[ $rc != 0 ]]; then return $rc; fi
-    hidl-gen -Landroidbp $root_arguments $additional_options $p;
-    rc=$?; if [[ $rc != 0 ]]; then return $rc; fi
-  done
-
-  local android_dirs=$(get_bp_dirs $current_dir) || return 1
-
-  echo "Updating Android.bp files."
-
-  for bp_dir in $android_dirs; do
-    bp="$bp_dir/Android.bp"
-    # locations of Android.bp files in specific subdirectory of frameworks/hardware/interfaces
-    android_bps=$(find $bp_dir                   \
-                  -name "Android.bp"             \
-                  ! -path $bp_dir/Android.bp     \
-                  -printf "%h\n"                 \
-                  | sort)
-
-    echo "// This is an autogenerated file, do not edit." > "$bp";
-    echo "subdirs = [" >> "$bp";
-    for a in $android_bps; do
-      echo "    \"${a#$bp_dir/}\"," >> "$bp";
-    done
-    echo "]" >> "$bp";
   done
 }
diff --git a/utils/Android.bp b/utils/Android.bp
index ff171d3..7d97c75 100644
--- a/utils/Android.bp
+++ b/utils/Android.bp
@@ -15,16 +15,15 @@
 cc_library {
     name: "libhidl-gen-utils",
     host_supported: true,
-    vendor_available: true,
-    cflags: hidl_flags,
+    defaults: ["hidl-gen-defaults"],
     srcs: [
         "FQName.cpp",
         "Formatter.cpp",
+        "FqInstance.cpp",
         "StringHelper.cpp",
     ],
     shared_libs: [
         "libbase",
-        "liblog",
     ],
 
     local_include_dirs: ["include/hidl-util"],
diff --git a/utils/FQName.cpp b/utils/FQName.cpp
index 630fff4..8f56af8 100644
--- a/utils/FQName.cpp
+++ b/utils/FQName.cpp
@@ -29,27 +29,6 @@
 #define RE_MAJOR        "[0-9]+"
 #define RE_MINOR        "[0-9]+"
 
-// android.hardware.foo@1.0::IFoo.Type
-static const std::regex kRE1("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH ")");
-// @1.0::IFoo.Type
-static const std::regex kRE2("@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH ")");
-// android.hardware.foo@1.0 (for package declaration and whole package import)
-static const std::regex kRE3("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")");
-// IFoo.Type
-static const std::regex kRE4("(" RE_COMPONENT ")([.]" RE_COMPONENT ")+");
-// Type (a plain identifier)
-static const std::regex kRE5("(" RE_COMPONENT ")");
-
-// android.hardware.foo@1.0::IFoo.Type:MY_ENUM_VALUE
-static const std::regex kRE6("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH "):(" RE_COMPONENT ")");
-// @1.0::IFoo.Type:MY_ENUM_VALUE
-static const std::regex kRE7("@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH "):(" RE_COMPONENT ")");
-// IFoo.Type:MY_ENUM_VALUE
-static const std::regex kRE8("(" RE_PATH "):(" RE_COMPONENT ")");
-
-// 1.0
-static const std::regex kREVer("(" RE_MAJOR ")[.](" RE_MINOR ")");
-
 namespace android {
 
 FQName::FQName()
@@ -57,10 +36,15 @@
       mIsIdentifier(false) {
 }
 
+// TODO(b/73774955): delete
 FQName::FQName(const std::string &s)
     : mValid(false),
       mIsIdentifier(false) {
-    setTo(s);
+    (void)setTo(s);
+}
+
+bool FQName::parse(const std::string& s, FQName* into) {
+    return into->setTo(s);
 }
 
 FQName::FQName(
@@ -73,12 +57,12 @@
       mPackage(package),
       mName(name),
       mValueName(valueName) {
-    setVersion(version);
+    CHECK(setVersion(version)) << version;
 
     // Check if this is actually a valid fqName
     FQName other;
-    other.setTo(this->string());
-    CHECK(other.mValid && (*this) == other);
+    CHECK(parse(this->string(), &other)) << this->string();
+    CHECK((*this) == other) << this->string() << " " << other.string();
 }
 
 FQName::FQName(const FQName& other)
@@ -91,12 +75,6 @@
       mValueName(other.mValueName) {
 }
 
-FQName::FQName(const std::vector<std::string> &names)
-    : mValid(false),
-      mIsIdentifier(false) {
-    setTo(StringHelper::JoinStrings(names, "."));
-}
-
 bool FQName::isValid() const {
     return mValid;
 }
@@ -114,30 +92,51 @@
         || (!mName.empty() && !mValueName.empty());
 }
 
-bool FQName::setTo(const std::string &s) {
-    clearVersion();
-    mPackage.clear();
-    mName.clear();
+bool FQName::isInterfaceName() const {
+    return !mName.empty() && mName[0] == 'I' && mName.find('.') == std::string::npos;
+}
 
-    mValid = true;
+bool FQName::setTo(const std::string &s) {
+    // android.hardware.foo@1.0::IFoo.Type
+    static const std::regex kRE1("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH ")");
+    // @1.0::IFoo.Type
+    static const std::regex kRE2("@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH ")");
+    // android.hardware.foo@1.0 (for package declaration and whole package import)
+    static const std::regex kRE3("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")");
+    // IFoo.Type
+    static const std::regex kRE4("(" RE_COMPONENT ")([.]" RE_COMPONENT ")+");
+    // Type (a plain identifier)
+    static const std::regex kRE5("(" RE_COMPONENT ")");
+
+    // android.hardware.foo@1.0::IFoo.Type:MY_ENUM_VALUE
+    static const std::regex kRE6("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH
+                                 "):(" RE_COMPONENT ")");
+    // @1.0::IFoo.Type:MY_ENUM_VALUE
+    static const std::regex kRE7("@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH "):(" RE_COMPONENT
+                                 ")");
+    // IFoo.Type:MY_ENUM_VALUE
+    static const std::regex kRE8("(" RE_PATH "):(" RE_COMPONENT ")");
+
+    bool invalid = false;
+    clear();
 
     std::smatch match;
     if (std::regex_match(s, match, kRE1)) {
         CHECK_EQ(match.size(), 5u);
 
         mPackage = match.str(1);
-        parseVersion(match.str(2), match.str(3));
+        invalid |= !parseVersion(match.str(2), match.str(3));
         mName = match.str(4);
     } else if (std::regex_match(s, match, kRE2)) {
         CHECK_EQ(match.size(), 4u);
 
-        parseVersion(match.str(1), match.str(2));
+        invalid |= !parseVersion(match.str(1), match.str(2));
         mName = match.str(3);
     } else if (std::regex_match(s, match, kRE3)) {
         CHECK_EQ(match.size(), 4u);
 
         mPackage = match.str(1);
-        parseVersion(match.str(2), match.str(3));
+        invalid |= !parseVersion(match.str(2), match.str(3));
     } else if (std::regex_match(s, match, kRE4)) {
         mName = match.str(0);
     } else if (std::regex_match(s, match, kRE5)) {
@@ -147,13 +146,13 @@
         CHECK_EQ(match.size(), 6u);
 
         mPackage = match.str(1);
-        parseVersion(match.str(2), match.str(3));
+        invalid |= !parseVersion(match.str(2), match.str(3));
         mName = match.str(4);
         mValueName = match.str(5);
     } else if (std::regex_match(s, match, kRE7)) {
         CHECK_EQ(match.size(), 5u);
 
-        parseVersion(match.str(1), match.str(2));
+        invalid |= !parseVersion(match.str(1), match.str(2));
         mName = match.str(3);
         mValueName = match.str(4);
     } else if (std::regex_match(s, match, kRE8)) {
@@ -162,19 +161,21 @@
         mName = match.str(1);
         mValueName = match.str(2);
     } else {
-        mValid = false;
+        invalid = true;
     }
 
     // mValueName must go with mName.
     CHECK(mValueName.empty() || !mName.empty());
 
     // package without version is not allowed.
-    CHECK(mPackage.empty() || !version().empty());
+    CHECK(invalid || mPackage.empty() || !version().empty());
 
-    return isValid();
+    // TODO(b/73774955): remove isValid and users
+    // of old FQName constructors
+    return mValid = !invalid;
 }
 
-std::string FQName::package() const {
+const std::string& FQName::package() const {
     return mPackage;
 }
 
@@ -197,26 +198,37 @@
     return v.empty() ? "" : ("@" + v);
 }
 
-void FQName::setVersion(const std::string &v) {
+void FQName::clear() {
+    mValid = true;
+    mIsIdentifier = false;
+    mPackage.clear();
+    clearVersion();
+    mName.clear();
+    mValueName.clear();
+}
+
+bool FQName::setVersion(const std::string& v) {
+    static const std::regex kREVer("(" RE_MAJOR ")[.](" RE_MINOR ")");
+
     if (v.empty()) {
         clearVersion();
-        return;
+        return true;
     }
-    std::smatch match;
-    if (std::regex_match(v, match, kREVer)) {
-        CHECK_EQ(match.size(), 3u);
 
-        parseVersion(match.str(1), match.str(2));
-    } else {
-        mValid = false;
+    std::smatch match;
+    if (!std::regex_match(v, match, kREVer)) {
+        return mValid = false;
     }
+    CHECK_EQ(match.size(), 3u);
+
+    return parseVersion(match.str(1), match.str(2));
 }
 
 void FQName::clearVersion() {
     mMajor = mMinor = 0;
 }
 
-void FQName::parseVersion(const std::string &majorStr, const std::string &minorStr) {
+bool FQName::parseVersion(const std::string& majorStr, const std::string& minorStr) {
     bool versionParseSuccess =
         ::android::base::ParseUint(majorStr, &mMajor) &&
         ::android::base::ParseUint(minorStr, &mMinor);
@@ -224,9 +236,10 @@
         LOG(ERROR) << "numbers in " << majorStr << "." << minorStr << " are out of range.";
         mValid = false;
     }
+    return versionParseSuccess;
 }
 
-std::string FQName::name() const {
+const std::string& FQName::name() const {
     return mName;
 }
 
@@ -240,7 +253,7 @@
     return res;
 }
 
-std::string FQName::valueName() const {
+const std::string& FQName::valueName() const {
     return mValueName;
 }
 
@@ -260,12 +273,12 @@
     }
 
     if (version().empty()) {
-        setVersion(defaultVersion);
+        CHECK(setVersion(defaultVersion));
     }
 }
 
 std::string FQName::string() const {
-    CHECK(mValid);
+    CHECK(mValid) << mPackage << atVersion() << mName;
 
     std::string out;
     out.append(mPackage);
@@ -285,15 +298,6 @@
     return out;
 }
 
-void FQName::print() const {
-    if (!mValid) {
-        LOG(INFO) << "INVALID";
-        return;
-    }
-
-    LOG(INFO) << string();
-}
-
 bool FQName::operator<(const FQName &other) const {
     return string() < other.string();
 }
@@ -306,9 +310,8 @@
     return !(*this == other);
 }
 
-std::string FQName::getInterfaceName() const {
-    CHECK(names().size() == 1) << "Must be a top level type";
-    CHECK(!mName.empty() && mName[0] == 'I') << mName;
+const std::string& FQName::getInterfaceName() const {
+    CHECK(isInterfaceName()) << mName;
 
     return mName;
 }
@@ -318,6 +321,10 @@
     return getInterfaceName().substr(1);
 }
 
+std::string FQName::getInterfaceAdapterName() const {
+    return "A" + getInterfaceBaseName();
+}
+
 std::string FQName::getInterfaceHwName() const {
     return "IHw" + getInterfaceBaseName();
 }
@@ -338,6 +345,10 @@
     return FQName(package(), version(), getInterfaceProxyName());
 }
 
+FQName FQName::getInterfaceAdapterFqName() const {
+    return FQName(package(), version(), getInterfaceAdapterName());
+}
+
 FQName FQName::getInterfaceStubFqName() const {
     return FQName(package(), version(), getInterfaceStubName());
 }
@@ -449,6 +460,13 @@
     return mMajor > 0;
 }
 
+FQName FQName::withVersion(size_t major, size_t minor) const {
+    FQName ret(*this);
+    ret.mMajor = major;
+    ret.mMinor = minor;
+    return ret;
+}
+
 size_t FQName::getPackageMajorVersion() const {
     CHECK(hasVersion()) << "FQName: No version exists at getPackageMajorVersion(). "
                         << "Did you check hasVersion()?";
@@ -524,5 +542,8 @@
     return ret;
 }
 
+const FQName gIBaseFqName = FQName("android.hidl.base", "1.0", "IBase");
+const FQName gIManagerFqName = FQName("android.hidl.manager", "1.0", "IServiceManager");
+
 }  // namespace android
 
diff --git a/utils/Formatter.cpp b/utils/Formatter.cpp
index a4f2047..338cb17 100644
--- a/utils/Formatter.cpp
+++ b/utils/Formatter.cpp
@@ -18,13 +18,17 @@
 
 #include <assert.h>
 
+#include <android-base/logging.h>
+
 namespace android {
 
-Formatter::Formatter(FILE *file)
+Formatter::Formatter() : mFile(NULL /* invalid */), mIndentDepth(0), mAtStartOfLine(true) {}
+
+Formatter::Formatter(FILE* file, size_t spacesPerIndent)
     : mFile(file == NULL ? stdout : file),
       mIndentDepth(0),
-      mAtStartOfLine(true) {
-}
+      mSpacesPerIndent(spacesPerIndent),
+      mAtStartOfLine(true) {}
 
 Formatter::~Formatter() {
     if (mFile != stdout) {
@@ -42,18 +46,18 @@
     mIndentDepth -= level;
 }
 
-Formatter &Formatter::indent(size_t level, std::function<void(void)> func) {
+Formatter& Formatter::indent(size_t level, const std::function<void(void)>& func) {
     this->indent(level);
     func();
     this->unindent(level);
     return *this;
 }
 
-Formatter &Formatter::indent(std::function<void(void)> func) {
+Formatter& Formatter::indent(const std::function<void(void)>& func) {
     return this->indent(1, func);
 }
 
-Formatter &Formatter::block(std::function<void(void)> func) {
+Formatter& Formatter::block(const std::function<void(void)>& func) {
     (*this) << "{\n";
     this->indent(func);
     return (*this) << "}";
@@ -71,42 +75,42 @@
     return (*this) << "\n";
 }
 
-Formatter &Formatter::sIf(const std::string &cond, std::function<void(void)> block) {
+Formatter& Formatter::sIf(const std::string& cond, const std::function<void(void)>& block) {
     (*this) << "if (" << cond << ") ";
     return this->block(block);
 }
 
-Formatter &Formatter::sElseIf(const std::string &cond, std::function<void(void)> block) {
+Formatter& Formatter::sElseIf(const std::string& cond, const std::function<void(void)>& block) {
     (*this) << " else if (" << cond << ") ";
     return this->block(block);
 }
 
-Formatter &Formatter::sElse(std::function<void(void)> block) {
+Formatter& Formatter::sElse(const std::function<void(void)>& block) {
     (*this) << " else ";
     return this->block(block);
 }
 
-Formatter &Formatter::sFor(const std::string &stmts, std::function<void(void)> block) {
+Formatter& Formatter::sFor(const std::string& stmts, const std::function<void(void)>& block) {
     (*this) << "for (" << stmts << ") ";
     return this->block(block);
 }
 
-Formatter &Formatter::sTry(std::function<void(void)> block) {
+Formatter& Formatter::sTry(const std::function<void(void)>& block) {
     (*this) << "try ";
     return this->block(block);
 }
 
-Formatter &Formatter::sCatch(const std::string &exception, std::function<void(void)> block) {
+Formatter& Formatter::sCatch(const std::string& exception, const std::function<void(void)>& block) {
     (*this) << " catch (" << exception << ") ";
     return this->block(block);
 }
 
-Formatter &Formatter::sFinally(std::function<void(void)> block) {
+Formatter& Formatter::sFinally(const std::function<void(void)>& block) {
     (*this) << " finally ";
     return this->block(block);
 }
 
-Formatter &Formatter::sWhile(const std::string &cond, std::function<void(void)> block) {
+Formatter& Formatter::sWhile(const std::string& cond, const std::function<void(void)>& block) {
     (*this) << "while (" << cond << ") ";
     return this->block(block);
 }
@@ -115,12 +119,12 @@
     const size_t len = out.length();
     size_t start = 0;
     while (start < len) {
-        size_t pos = out.find("\n", start);
+        size_t pos = out.find('\n', start);
 
         if (pos == std::string::npos) {
             if (mAtStartOfLine) {
+                fprintf(mFile, "%*s", (int)(mSpacesPerIndent * mIndentDepth), "");
                 fprintf(mFile, "%s", mLinePrefix.c_str());
-                fprintf(mFile, "%*s", (int)(4 * mIndentDepth), "");
                 mAtStartOfLine = false;
             }
 
@@ -128,17 +132,16 @@
             break;
         }
 
+        if (mAtStartOfLine && (pos > start || !mLinePrefix.empty())) {
+            fprintf(mFile, "%*s", (int)(mSpacesPerIndent * mIndentDepth), "");
+            fprintf(mFile, "%s", mLinePrefix.c_str());
+        }
+
         if (pos == start) {
             fprintf(mFile, "\n");
             mAtStartOfLine = true;
         } else if (pos > start) {
-            if (mAtStartOfLine) {
-                fprintf(mFile, "%s", mLinePrefix.c_str());
-                fprintf(mFile, "%*s", (int)(4 * mIndentDepth), "");
-            }
-
             output(out.substr(start, pos - start + 1));
-
             mAtStartOfLine = true;
         }
 
@@ -148,10 +151,11 @@
     return *this;
 }
 
-#define FORMATTER_INPUT_INTEGER(__type__)               \
-    Formatter &Formatter::operator<<(__type__ n) {      \
-        return (*this) << std::to_string(n);            \
-    }                                                   \
+// NOLINT to suppress missing parentheses warning about __type__.
+#define FORMATTER_INPUT_INTEGER(__type__)                       \
+    Formatter& Formatter::operator<<(__type__ n) { /* NOLINT */ \
+        return (*this) << std::to_string(n);                    \
+    }
 
 FORMATTER_INPUT_INTEGER(short);
 FORMATTER_INPUT_INTEGER(unsigned short);
@@ -167,10 +171,11 @@
 
 #undef FORMATTER_INPUT_INTEGER
 
-#define FORMATTER_INPUT_CHAR(__type__)                  \
-    Formatter &Formatter::operator<<(__type__ c) {      \
-        return (*this) << std::string(1, (char)c);    \
-    }                                                   \
+// NOLINT to suppress missing parentheses warning about __type__.
+#define FORMATTER_INPUT_CHAR(__type__)                          \
+    Formatter& Formatter::operator<<(__type__ c) { /* NOLINT */ \
+        return (*this) << std::string(1, (char)c);              \
+    }
 
 FORMATTER_INPUT_CHAR(char);
 FORMATTER_INPUT_CHAR(signed char);
@@ -182,24 +187,12 @@
     mSpace = space;
 }
 
+bool Formatter::isValid() const {
+    return mFile != nullptr;
+}
+
 void Formatter::output(const std::string &text) const {
-    const size_t spaceLength = mSpace.size();
-    if (spaceLength > 0) {
-        // Remove all occurences of "mSpace" and output the filtered result.
-        size_t matchPos = text.find(mSpace);
-        if (matchPos != std::string::npos) {
-            std::string newText = text.substr(0, matchPos);
-            size_t startPos = matchPos + spaceLength;
-            while ((matchPos = text.find(mSpace, startPos))
-                    != std::string::npos) {
-                newText.append(text.substr(startPos, matchPos - startPos));
-                startPos = matchPos + spaceLength;
-            }
-            newText.append(text.substr(startPos));
-            fprintf(mFile, "%s", newText.c_str());
-            return;
-        }
-    }
+    CHECK(isValid());
 
     fprintf(mFile, "%s", text.c_str());
 }
diff --git a/utils/FqInstance.cpp b/utils/FqInstance.cpp
new file mode 100644
index 0000000..0b2b8f8
--- /dev/null
+++ b/utils/FqInstance.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FqInstance.h"
+
+#include <sstream>
+
+namespace android {
+
+static const char INSTANCE_SEP = '/';
+
+const std::string& FqInstance::getPackage() const {
+    return mFqName.package();
+}
+
+bool FqInstance::hasPackage() const {
+    return !getPackage().empty();
+}
+
+size_t FqInstance::getMajorVersion() const {
+    return hasVersion() ? mFqName.getPackageMajorVersion() : 0;
+}
+
+size_t FqInstance::getMinorVersion() const {
+    return hasVersion() ? mFqName.getPackageMinorVersion() : 0;
+}
+
+std::pair<size_t, size_t> FqInstance::getVersion() const {
+    return {getMajorVersion(), getMinorVersion()};
+}
+
+bool FqInstance::hasVersion() const {
+    return mFqName.hasVersion();
+}
+
+const std::string& FqInstance::getInterface() const {
+    return mFqName.getInterfaceName();
+}
+
+bool FqInstance::hasInterface() const {
+    return mFqName.isInterfaceName();
+}
+
+const std::string& FqInstance::getInstance() const {
+    return mInstance;
+}
+
+bool FqInstance::hasInstance() const {
+    return !mInstance.empty();
+}
+
+bool FqInstance::setTo(const std::string& s) {
+    auto pos = s.find(INSTANCE_SEP);
+    if (!mFqName.setTo(s.substr(0, pos))) return false;
+    mInstance = pos == std::string::npos ? std::string{} : s.substr(pos + 1);
+
+    bool hasPkg = hasPackage();
+    bool hasVer = hasVersion();
+    bool hasIntf = hasInterface();
+    bool hasInst = hasInstance();
+
+    // android.hardware.foo@1.0::IFoo/default
+    if (hasPkg && hasVer && hasIntf && hasInst) {
+        return true;
+    }
+
+    // @1.0::IFoo/default
+    if (!hasPkg && hasVer && hasIntf && hasInst) {
+        return true;
+    }
+
+    // IFoo/default
+    if (!hasPkg && !hasVer && hasIntf && hasInst) {
+        return true;
+    }
+
+    // Other cases are covered by FQName::setTo, but instance name should be empty.
+    return !hasInst;
+}
+
+bool FqInstance::setTo(const std::string& package, size_t majorVer, size_t minorVer,
+                       const std::string& interface, const std::string& instance) {
+    std::stringstream ss;
+    ss << package << "@" << majorVer << "." << minorVer << "::" << interface << INSTANCE_SEP
+       << instance;
+    return setTo(ss.str());
+}
+
+bool FqInstance::setTo(size_t majorVer, size_t minorVer, const std::string& interface,
+                       const std::string& instance) {
+    return setTo("", majorVer, minorVer, interface, instance);
+}
+
+bool FqInstance::setTo(const std::string& interface, const std::string& instance) {
+    return setTo(interface + INSTANCE_SEP + instance);
+}
+
+std::string FqInstance::string() const {
+    std::string ret = mFqName.string();
+    if (hasInstance()) ret += INSTANCE_SEP + mInstance;
+    return ret;
+}
+
+bool FqInstance::operator<(const FqInstance& other) const {
+    return string() < other.string();
+}
+
+bool FqInstance::operator==(const FqInstance& other) const {
+    return string() == other.string();
+}
+
+bool FqInstance::operator!=(const FqInstance& other) const {
+    return !(*this == other);
+}
+
+}  // namespace android
diff --git a/utils/StringHelper.cpp b/utils/StringHelper.cpp
index 3718d53..aae9b5a 100644
--- a/utils/StringHelper.cpp
+++ b/utils/StringHelper.cpp
@@ -184,6 +184,10 @@
 }
 
 std::string StringHelper::RTrimAll(const std::string &in, const std::string &suffix) {
+    if (suffix.empty()) {
+        return in;
+    }
+
     std::string copy(in);
     while (EndsWith(copy, suffix)) {
         copy = copy.substr(0, copy.size() - suffix.size());
@@ -193,6 +197,10 @@
 }
 
 std::string StringHelper::LTrimAll(const std::string &in, const std::string &prefix) {
+    if (prefix.empty()) {
+        return in;
+    }
+
     std::string copy(in);
     while (StartsWith(copy, prefix)) {
         copy = copy.substr(prefix.size());
diff --git a/utils/include/hidl-util/FQName.h b/utils/include/hidl-util/FQName.h
index 3cdc7a9..3e31431 100644
--- a/utils/include/hidl-util/FQName.h
+++ b/utils/include/hidl-util/FQName.h
@@ -18,35 +18,35 @@
 
 #define FQNAME_H_
 
-#include <android-base/macros.h>
 #include <string>
 #include <vector>
 
 namespace android {
 
 struct FQName {
+    __attribute__((warn_unused_result)) static bool parse(const std::string& s, FQName* into);
+
     explicit FQName();
+
+    // TODO(b/73774955): delete
     explicit FQName(const std::string &s);
 
-    FQName(const std::string &package,
-           const std::string &version,
-           const std::string &name,
-           const std::string &valueName = "");
-
-    // a synonym to FQName(names.join("."))
-    FQName(const std::vector<std::string> &names);
+    FQName(const std::string& package, const std::string& version, const std::string& name = "",
+           const std::string& valueName = "");
 
     FQName(const FQName& other);
 
     bool isValid() const;
     bool isIdentifier() const;
-    bool setTo(const std::string &s);
+
+    // Returns false if string isn't a valid FQName object.
+    __attribute__((warn_unused_result)) bool setTo(const std::string& s);
 
     void applyDefaults(
             const std::string &defaultPackage,
             const std::string &defaultVersion);
 
-    std::string package() const;
+    const std::string& package() const;
     // Return version in the form "@1.0" if it is present, otherwise empty string.
     std::string atVersion() const;
     // Return version in the form "1.0" if it is present, otherwise empty string.
@@ -56,6 +56,8 @@
     // Return true only if version is present.
     bool hasVersion() const;
 
+    FQName withVersion(size_t major, size_t minor) const;
+
     // The next two methods return the name part of the FQName, that is, the
     // part after the version field.  For example:
     //
@@ -80,13 +82,13 @@
     // FQName::name() will return "IFoo.bar.baz". FQName::names() will return
     // std::vector<std::string>{"IFoo","bar","baz"}
 
-    std::string name() const;
+    const std::string& name() const;
     std::vector<std::string> names() const;
 
     // The next two methods returns two parts of the FQName, that is,
     // the first part package + version + name, the second part valueName.
     FQName typeName() const;
-    std::string valueName() const;
+    const std::string& valueName() const;
 
     // has package version and name
     bool isFullyQualified() const;
@@ -96,7 +98,9 @@
     // 2. (valueName), aka a single identifier
     bool isValidValueName() const;
 
-    void print() const;
+    // Interface names start with 'I'
+    bool isInterfaceName() const;
+
     std::string string() const;
 
     bool operator<(const FQName &other) const;
@@ -110,8 +114,13 @@
 
     // Must be called on an interface
     // android.hardware.foo@1.0::IBar
+    // -> ABar
+    std::string getInterfaceAdapterName() const;
+
+    // Must be called on an interface
+    // android.hardware.foo@1.0::IBar
     // -> IBar
-    std::string getInterfaceName() const;
+    const std::string& getInterfaceName() const;
 
     // Must be called on an interface
     // android.hardware.foo@1.0::IBar
@@ -120,12 +129,12 @@
 
     // Must be called on an interface
     // android.hardware.foo@1.0::IBar
-    // -> BpBar
+    // -> BpHwBar
     std::string getInterfaceProxyName() const;
 
     // Must be called on an interface
     // android.hardware.foo@1.0::IBar
-    // -> BnBar
+    // -> BnHwBar
     std::string getInterfaceStubName() const;
 
     // Must be called on an interface
@@ -140,6 +149,11 @@
 
     // Must be called on an interface
     // android.hardware.foo@1.0::IBar
+    // -> android.hardware.foo@1.0::ABar
+    FQName getInterfaceAdapterFqName() const;
+
+    // Must be called on an interface
+    // android.hardware.foo@1.0::IBar
     // -> android.hardware.foo@1.0::BnBar
     FQName getInterfaceStubFqName() const;
 
@@ -210,8 +224,10 @@
     // minor-- if result doesn't underflow, else abort.
     FQName downRev() const;
 
-private:
+   private:
+    // TODO(b/73774955): remove
     bool mValid;
+
     bool mIsIdentifier;
     std::string mPackage;
     // mMajor == 0 means empty.
@@ -220,15 +236,16 @@
     std::string mName;
     std::string mValueName;
 
-    void setVersion(const std::string &v);
+    void clear();
+
+    __attribute__((warn_unused_result)) bool setVersion(const std::string& v);
+    __attribute__((warn_unused_result)) bool parseVersion(const std::string& majorStr,
+                                                          const std::string& minorStr);
     void clearVersion();
-    void parseVersion(const std::string &majorStr, const std::string &minorStr);
 };
 
-static const FQName gIBaseFqName = FQName{"android.hidl.base@1.0::IBase"};
-static const FQName gIBasePackageFqName = FQName{"android.hidl.base"};
-static const FQName gIManagerFqName = FQName{"android.hidl.manager@1.0::IServiceManager"};
-static const FQName gIManagerPackageFqName = FQName{"android.hidl.manager"};
+extern const FQName gIBaseFqName;
+extern const FQName gIManagerFqName;
 
 }  // namespace android
 
diff --git a/utils/include/hidl-util/Formatter.h b/utils/include/hidl-util/Formatter.h
index bb4288b..053563f 100644
--- a/utils/include/hidl-util/Formatter.h
+++ b/utils/include/hidl-util/Formatter.h
@@ -18,7 +18,6 @@
 
 #define FORMATTER_H_
 
-#include <android-base/macros.h>
 #include <functional>
 #include <string>
 
@@ -30,9 +29,11 @@
 // The other is with chain calls and lambda functions
 //     out.sIf("good", [&] { out("blah").endl()("blah").endl(); }).endl();
 struct Formatter {
+    static Formatter invalid() { return Formatter(); }
 
     // Assumes ownership of file. Directed to stdout if file == NULL.
-    Formatter(FILE *file = NULL);
+    Formatter(FILE* file, size_t spacesPerIndent = 4);
+    Formatter(Formatter&&) = default;
     ~Formatter();
 
     void indent(size_t level = 1);
@@ -42,13 +43,13 @@
     // out.indent(2, [&] {
     //     out << "Meow\n";
     // });
-    Formatter &indent(size_t level, std::function<void(void)> func);
+    Formatter& indent(size_t level, const std::function<void(void)>& func);
 
     // Note that The last \n after the last line is NOT added automatically.
     // out.indent([&] {
     //     out << "Meow\n";
     // });
-    Formatter &indent(std::function<void(void)> func);
+    Formatter& indent(const std::function<void(void)>& func);
 
     // A block inside braces.
     // * No space will be added before the opening brace.
@@ -62,7 +63,7 @@
     // out << "{\n"
     //     << "one();\ntwo();\n" // func()
     //     << "}";
-    Formatter &block(std::function<void(void)> func);
+    Formatter& block(const std::function<void(void)>& func);
 
     // A synonym to (*this) << "\n";
     Formatter &endl();
@@ -75,14 +76,14 @@
     //     out << "logFatal();\n";
     // }).endl();
     // note that there will be a space before the "else"-s.
-    Formatter &sIf(const std::string &cond, std::function<void(void)> block);
-    Formatter &sElseIf(const std::string &cond, std::function<void(void)> block);
-    Formatter &sElse(std::function<void(void)> block);
+    Formatter& sIf(const std::string& cond, const std::function<void(void)>& block);
+    Formatter& sElseIf(const std::string& cond, const std::function<void(void)>& block);
+    Formatter& sElse(const std::function<void(void)>& block);
 
     // out.sFor("int i = 0; i < 10; i++", [&] {
     //     out << "printf(\"%d\", i);\n";
     // }).endl();
-    Formatter &sFor(const std::string &stmts, std::function<void(void)> block);
+    Formatter& sFor(const std::string& stmts, const std::function<void(void)>& block);
 
     // out.sTry([&] {
     //     out << "throw RemoteException();\n"
@@ -92,21 +93,22 @@
     //     // cleanup
     // }).endl();
     // note that there will be a space before the "catch"-s.
-    Formatter &sTry(std::function<void(void)> block);
-    Formatter &sCatch(const std::string &exception, std::function<void(void)> block);
-    Formatter &sFinally(std::function<void(void)> block);
+    Formatter& sTry(const std::function<void(void)>& block);
+    Formatter& sCatch(const std::string& exception, const std::function<void(void)>& block);
+    Formatter& sFinally(const std::function<void(void)>& block);
 
     // out.sWhile("z < 10", [&] {
     //     out << "z++;\n";
     // }).endl();
-    Formatter &sWhile(const std::string &cond, std::function<void(void)> block);
+    Formatter& sWhile(const std::string& cond, const std::function<void(void)>& block);
 
     // out.join(v.begin(), v.end(), ",", [&](const auto &e) {
     //     out << toString(e);
     // });
-    template<typename I>
-    Formatter &join(const I begin, const I end, const std::string &separator,
-            std::function<void(const typename std::iterator_traits<I>::value_type &)> func);
+    template <typename I>
+    Formatter& join(
+        const I begin, const I end, const std::string& separator,
+        const std::function<void(const typename std::iterator_traits<I>::value_type&)>& func);
 
     Formatter &operator<<(const std::string &out);
 
@@ -137,9 +139,15 @@
     // Remove the line prefix.
     void unsetLinePrefix();
 
-private:
-    FILE *mFile;
+    bool isValid() const;
+
+   private:
+    // Creates an invalid formatter object.
+    Formatter();
+
+    FILE* mFile;  // invalid if nullptr
     size_t mIndentDepth;
+    size_t mSpacesPerIndent;
     bool mAtStartOfLine;
 
     std::string mSpace;
@@ -147,12 +155,14 @@
 
     void output(const std::string &text) const;
 
-    DISALLOW_COPY_AND_ASSIGN(Formatter);
+    Formatter(const Formatter&) = delete;
+    void operator=(const Formatter&) = delete;
 };
 
-template<typename I>
-Formatter &Formatter::join(const I begin, const I end, const std::string &separator,
-        std::function<void(const typename std::iterator_traits<I>::value_type &)> func) {
+template <typename I>
+Formatter& Formatter::join(
+    const I begin, const I end, const std::string& separator,
+    const std::function<void(const typename std::iterator_traits<I>::value_type&)>& func) {
     for (I iter = begin; iter != end; ++iter) {
         if (iter != begin) {
             (*this) << separator;
diff --git a/utils/include/hidl-util/FqInstance.h b/utils/include/hidl-util/FqInstance.h
new file mode 100644
index 0000000..bdd0043
--- /dev/null
+++ b/utils/include/hidl-util/FqInstance.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FQINSTANCE_H_
+
+#define ANDROID_FQINSTANCE_H_
+
+#include <string>
+#include <utility>
+
+#include <hidl-util/FQName.h>
+
+namespace android {
+
+// A wrapper around FQName to include instance name as well.
+// FqInstance::setTo also recognizes all FQName formats, including enum names
+// etc.
+// Typical usage:
+// FqInstance fqInstance;
+// if (!fqInstance.setTo("...")) {
+//    // error handling
+// }
+// LOG(WARNING) << fqInstance.string();
+class FqInstance {
+   public:
+    const std::string& getPackage() const;
+    size_t getMajorVersion() const;
+    size_t getMinorVersion() const;
+    std::pair<size_t, size_t> getVersion() const;
+    const std::string& getInterface() const;
+    const std::string& getInstance() const;
+
+    bool hasPackage() const;
+    bool hasVersion() const;
+    bool hasInterface() const;
+    bool hasInstance() const;
+
+    // Return true if valid:
+    // android.hardware.foo@1.0::IFoo/instance
+    // @1.0::IFoo/instance
+    // IFoo/instance
+    // android.hardware.foo@1.0::IFoo.Type
+    // @1.0::IFoo.Type
+    // android.hardware.foo@1.0
+    // IFoo.Type
+    // Type
+    // android.hardware.foo@1.0::IFoo.Type:MY_ENUM_VALUE
+    // @1.0::IFoo.Type:MY_ENUM_VALUE
+    // IFoo.Type:MY_ENUM_VALUE
+    //
+    // If no "/instance", hasInstance() will return false afterwards.
+    // TODO(b/73774955): deprecate this and use std::optional.
+    __attribute__((warn_unused_result)) bool setTo(const std::string& s);
+
+    // Convenience method for the following formats:
+    // android.hardware.foo@1.0::IFoo/default
+    // @1.0::IFoo/default
+    // IFoo/default
+    __attribute__((warn_unused_result)) bool setTo(const std::string& package, size_t majorVer,
+                                                   size_t minorVer, const std::string& interface,
+                                                   const std::string& instance);
+    __attribute__((warn_unused_result)) bool setTo(size_t majorVer, size_t minorVer,
+                                                   const std::string& interface,
+                                                   const std::string& instance);
+    __attribute__((warn_unused_result)) bool setTo(const std::string& interface,
+                                                   const std::string& instance);
+
+    // undefined behavior if:
+    // - Default constructor is called without setTo();
+    // - setTo is called but returned false.
+    // Should only be called after setTo() returns true.
+    std::string string() const;
+    bool operator<(const FqInstance& other) const;
+    bool operator==(const FqInstance& other) const;
+    bool operator!=(const FqInstance& other) const;
+
+   private:
+    FQName mFqName;
+    std::string mInstance;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_FQINSTANCE_H_
diff --git a/utils/include/hidl-util/StringHelper.h b/utils/include/hidl-util/StringHelper.h
index 277367b..51a47a0 100644
--- a/utils/include/hidl-util/StringHelper.h
+++ b/utils/include/hidl-util/StringHelper.h
@@ -19,7 +19,6 @@
 #define STRING_HELPER_H_
 
 #include <string>
-#include <android-base/macros.h>
 #include <vector>
 
 namespace android {
@@ -80,12 +79,10 @@
         const std::string &separator);
 
 private:
-    StringHelper() {}
+    StringHelper() = delete;
 
     static void Tokenize(const std::string &in,
         std::vector<std::string> *vec);
-
-    DISALLOW_COPY_AND_ASSIGN(StringHelper);
 };
 
 }  // namespace android