Add -Lformat option to format .hal files

Formats .hal files.

Bug: 137553653
Test: hidl-gen -Lformat <IFACE>
Test: aosp/1051701
Test: hidl-gen -Lformat (google-interfaces) & hidl-gen -Lcheck
(google-interfaces)
Change-Id: I5aa554d99fd4977acb7939d8c793081680a3f598
diff --git a/AST.h b/AST.h
index 47f0846..e9c1367 100644
--- a/AST.h
+++ b/AST.h
@@ -161,6 +161,8 @@
     void generateDependencies(Formatter& out) const;
     void generateInheritanceHierarchy(Formatter& out) const;
 
+    void generateFormattedHidl(Formatter& out) const;
+
     const std::vector<ImportStatement>& getImportStatements() const;
     void getImportedPackages(std::set<FQName> *importSet) const;
 
diff --git a/Android.bp b/Android.bp
index 830be46..b712dbe 100644
--- a/Android.bp
+++ b/Android.bp
@@ -110,6 +110,7 @@
         "generateCppAdapter.cpp",
         "generateCppImpl.cpp",
         "generateDependencies.cpp",
+        "generateFormattedHidl.cpp",
         "generateInheritanceHierarchy.cpp",
         "generateJava.cpp",
         "generateJavaImpl.cpp",
diff --git a/Annotation.cpp b/Annotation.cpp
index 438cff6..4c9637f 100644
--- a/Annotation.cpp
+++ b/Annotation.cpp
@@ -158,7 +158,7 @@
 
         const AnnotationParam* param = mParams->at(i);
 
-        out << param->getName() << "=";
+        out << param->getName() << " = ";
 
         const std::vector<std::string>& values = param->getValues();
         if (values.size() > 1) {
diff --git a/ArrayType.cpp b/ArrayType.cpp
index 33424d8..5e491cf 100644
--- a/ArrayType.cpp
+++ b/ArrayType.cpp
@@ -25,12 +25,15 @@
 namespace android {
 
 ArrayType::ArrayType(const Reference<Type>& elementType, ConstantExpression* size, Scope* parent)
-    : Type(parent), mElementType(elementType), mSizes{size} {
+    : Type(parent, elementType.localName()), mElementType(elementType) {
     CHECK(!elementType.isEmptyReference());
+
+    appendDimension(size);
 }
 
 void ArrayType::appendDimension(ConstantExpression *size) {
     mSizes.push_back(size);
+    mDefinedName = mDefinedName + "[" + size->expression() + "]";
 }
 
 size_t ArrayType::countDimensions() const {
diff --git a/CompoundType.cpp b/CompoundType.cpp
index 68bcedf..a431912 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -632,6 +632,24 @@
     }
 }
 
+void CompoundType::emitHidlDefinition(Formatter& out) const {
+    if (getDocComment() != nullptr) getDocComment()->emit(out);
+    out << typeName() << " ";
+
+    out.block([&] {
+        for (const Type* t : getSortedDefinedTypes()) {
+            t->emitHidlDefinition(out);
+        }
+
+        for (const NamedReference<Type>* ref : *mFields) {
+            if (ref->getDocComment() != nullptr) ref->getDocComment()->emit(out);
+            out << ref->localName() << " " << ref->name() << ";\n";
+        }
+    });
+
+    out << ";\n";
+}
+
 void CompoundType::emitTypeDeclarations(Formatter& out) const {
     if (mStyle == STYLE_SAFE_UNION) {
         emitSafeUnionTypeDeclarations(out);
diff --git a/CompoundType.h b/CompoundType.h
index 3dec6af..2c5a055 100644
--- a/CompoundType.h
+++ b/CompoundType.h
@@ -100,6 +100,7 @@
             const std::string &offset,
             bool isReader) const override;
 
+    void emitHidlDefinition(Formatter& out) const override;
     void emitTypeDeclarations(Formatter& out) const override;
     void emitTypeForwardDeclaration(Formatter& out) const override;
     void emitPackageTypeDeclarations(Formatter& out) const override;
diff --git a/ConstantExpression.cpp b/ConstantExpression.cpp
index 002bf56..6badf57 100644
--- a/ConstantExpression.cpp
+++ b/ConstantExpression.cpp
@@ -164,17 +164,19 @@
     return std::make_unique<LiteralConstantExpression>(kind, value);
 }
 
+ConstantExpression::ConstantExpression(const std::string& expr) : mExpr(expr) {}
+
 bool ConstantExpression::isEvaluated() const {
     return mIsEvaluated;
 }
 
-LiteralConstantExpression::LiteralConstantExpression(
-    ScalarType::Kind kind, uint64_t value, const std::string& expr) {
-
+LiteralConstantExpression::LiteralConstantExpression(ScalarType::Kind kind, uint64_t value,
+                                                     const std::string& expr)
+    : ConstantExpression(expr) {
     CHECK(!expr.empty());
     CHECK(isSupported(kind));
+
     mTrivialDescription = std::to_string(value) == expr;
-    mExpr = expr;
     mValueKind = kind;
     mValue = value;
     mIsEvaluated = true;
@@ -254,7 +256,6 @@
     CHECK(mUnary->isEvaluated());
     mIsEvaluated = true;
 
-    mExpr = std::string("(") + mOp + mUnary->mExpr + ")";
     mValueKind = mUnary->mValueKind;
 
 #define CASE_UNARY(__type__)                                          \
@@ -270,8 +271,6 @@
     CHECK(mRval->isEvaluated());
     mIsEvaluated = true;
 
-    mExpr = std::string("(") + mLval->mExpr + " " + mOp + " " + mRval->mExpr + ")";
-
     bool isArithmeticOrBitflip = OP_IS_BIN_ARITHMETIC || OP_IS_BIN_BITFLIP;
 
     // CASE 1: + - *  / % | ^ & < > <= >= == !=
@@ -330,8 +329,6 @@
     CHECK(mFalseVal->isEvaluated());
     mIsEvaluated = true;
 
-    mExpr = std::string("(") + mCond->mExpr + "?" + mTrueVal->mExpr + ":" + mFalseVal->mExpr + ")";
-
     // note: for ?:, unlike arithmetic ops, integral promotion is not processed.
     mValueKind = usualArithmeticConversion(mTrueVal->mValueKind, mFalseVal->mValueKind);
 
@@ -466,7 +463,6 @@
 }
 
 const std::string& ConstantExpression::expression() const {
-    CHECK(isEvaluated());
     return mExpr;
 }
 
@@ -683,7 +679,7 @@
 }
 
 UnaryConstantExpression::UnaryConstantExpression(const std::string& op, ConstantExpression* value)
-    : mUnary(value), mOp(op) {}
+    : ConstantExpression(std::string("(") + op + value->mExpr + ")"), mUnary(value), mOp(op) {}
 
 std::vector<const ConstantExpression*> UnaryConstantExpression::getConstantExpressions() const {
     return {mUnary};
@@ -691,7 +687,10 @@
 
 BinaryConstantExpression::BinaryConstantExpression(ConstantExpression* lval, const std::string& op,
                                                    ConstantExpression* rval)
-    : mLval(lval), mRval(rval), mOp(op) {}
+    : ConstantExpression(std::string("(") + lval->mExpr + " " + op + " " + rval->mExpr + ")"),
+      mLval(lval),
+      mRval(rval),
+      mOp(op) {}
 
 std::vector<const ConstantExpression*> BinaryConstantExpression::getConstantExpressions() const {
     return {mLval, mRval};
@@ -700,7 +699,11 @@
 TernaryConstantExpression::TernaryConstantExpression(ConstantExpression* cond,
                                                      ConstantExpression* trueVal,
                                                      ConstantExpression* falseVal)
-    : mCond(cond), mTrueVal(trueVal), mFalseVal(falseVal) {}
+    : ConstantExpression(std::string("(") + cond->mExpr + "?" + trueVal->mExpr + ":" +
+                         falseVal->mExpr + ")"),
+      mCond(cond),
+      mTrueVal(trueVal),
+      mFalseVal(falseVal) {}
 
 std::vector<const ConstantExpression*> TernaryConstantExpression::getConstantExpressions() const {
     return {mCond, mTrueVal, mFalseVal};
@@ -708,8 +711,7 @@
 
 ReferenceConstantExpression::ReferenceConstantExpression(const Reference<LocalIdentifier>& value,
                                                          const std::string& expr)
-    : mReference(value) {
-    mExpr = expr;
+    : ConstantExpression(expr), mReference(value) {
     mTrivialDescription = mExpr.empty();
 }
 
@@ -729,9 +731,7 @@
 AttributeConstantExpression::AttributeConstantExpression(const Reference<Type>& value,
                                                          const std::string& fqname,
                                                          const std::string& tag)
-    : mReference(value), mTag(tag) {
-    mExpr = fqname + "#" + tag;
-}
+    : ConstantExpression(fqname + "#" + tag), mReference(value), mTag(tag) {}
 
 std::vector<const ConstantExpression*> AttributeConstantExpression::getConstantExpressions() const {
     // Returns reference instead
diff --git a/ConstantExpression.h b/ConstantExpression.h
index b770716..9f20fd5 100644
--- a/ConstantExpression.h
+++ b/ConstantExpression.h
@@ -46,6 +46,7 @@
     static std::unique_ptr<ConstantExpression> One(ScalarType::Kind kind);
     static std::unique_ptr<ConstantExpression> ValueOf(ScalarType::Kind kind, uint64_t value);
 
+    ConstantExpression(const std::string& expr);
     virtual ~ConstantExpression() {}
 
     virtual bool isReferenceConstantExpression() const;
@@ -136,7 +137,7 @@
     /* If the result value has been evaluated. */
     bool mIsEvaluated = false;
     /* The formatted expression. */
-    std::string mExpr;
+    const std::string mExpr;
     /* The kind of the result value. */
     ScalarType::Kind mValueKind;
     /* The stored result value. */
diff --git a/DeathRecipientType.cpp b/DeathRecipientType.cpp
index abc69fc..1aa22ed 100644
--- a/DeathRecipientType.cpp
+++ b/DeathRecipientType.cpp
@@ -21,7 +21,7 @@
 
 namespace android {
 
-DeathRecipientType::DeathRecipientType(Scope* parent) : Type(parent) {}
+DeathRecipientType::DeathRecipientType(Scope* parent) : Type(parent, "death_recipient") {}
 
 std::string DeathRecipientType::typeName() const {
     return "death recipient";
diff --git a/DocComment.cpp b/DocComment.cpp
index 707679a..2a1c981 100644
--- a/DocComment.cpp
+++ b/DocComment.cpp
@@ -63,8 +63,16 @@
     mLocation.setLocation(mLocation.begin(), comment->mLocation.end());
 }
 
-void DocComment::emit(Formatter& out) const {
-    out << "/**\n";
+void DocComment::emit(Formatter& out, CommentType type) const {
+    switch (type) {
+        case CommentType::DOC_MULTILINE:
+            out << "/**\n";
+            break;
+        case CommentType::MULTILINE:
+            out << "/*\n";
+            break;
+    }
+
     out.setLinePrefix(" *");
 
     for (const std::string& line : mLines) {
diff --git a/DocComment.h b/DocComment.h
index 58c8e17..315ef60 100644
--- a/DocComment.h
+++ b/DocComment.h
@@ -27,12 +27,19 @@
 
 namespace android {
 
+enum class CommentType {
+    // multiline comment that begins with /**
+    DOC_MULTILINE,
+    // begins with /* (used for headers)
+    MULTILINE
+};
+
 struct DocComment {
     DocComment(const std::string& comment, const Location& location);
 
     void merge(const DocComment* comment);
 
-    void emit(Formatter& out) const;
+    void emit(Formatter& out, CommentType type = CommentType::DOC_MULTILINE) const;
 
     const std::vector<std::string>& lines() const { return mLines; }
 
diff --git a/EnumType.cpp b/EnumType.cpp
index 6a3a002..d0e88a3 100644
--- a/EnumType.cpp
+++ b/EnumType.cpp
@@ -251,6 +251,31 @@
             out, depth, parcelName, blobName, fieldName, offset, isReader);
 }
 
+void EnumType::emitHidlDefinition(Formatter& out) const {
+    if (getDocComment() != nullptr) getDocComment()->emit(out);
+
+    if (annotations().size() != 0) {
+        out.join(annotations().begin(), annotations().end(), " ",
+                 [&](auto annotation) { annotation->dump(out); });
+        out << "\n";
+    }
+
+    out << typeName() << " : " << mStorageType.localName() << " {\n";
+
+    out.indent([&] {
+        for (const EnumValue* val : mValues) {
+            if (val->getDocComment() != nullptr) val->getDocComment()->emit(out);
+            out << val->name();
+            if (!val->isAutoFill()) {
+                out << " = " << val->constExpr()->expression();
+            }
+            out << ",\n";
+        }
+    });
+
+    out << "};\n";
+}
+
 void EnumType::emitTypeDeclarations(Formatter& out) const {
     const ScalarType *scalarType = mStorageType->resolveToScalarType();
     CHECK(scalarType != nullptr);
@@ -783,7 +808,7 @@
     } else {
         std::string description = prevType->fullName() + "." + prevValue->name() + " implicitly";
         auto* prevReference = new ReferenceConstantExpression(
-            Reference<LocalIdentifier>(prevValue, mLocation), description);
+                Reference<LocalIdentifier>(prevValue->mName, prevValue, mLocation), description);
         mValue = prevReference->addOne(type->getKind()).release();
     }
 }
@@ -802,7 +827,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-BitFieldType::BitFieldType(Scope* parent) : TemplatedType(parent) {}
+BitFieldType::BitFieldType(Scope* parent) : TemplatedType(parent, "bitfield") {}
 
 bool BitFieldType::isBitField() const {
     return true;
diff --git a/EnumType.h b/EnumType.h
index b429e39..ab796d1 100644
--- a/EnumType.h
+++ b/EnumType.h
@@ -96,6 +96,7 @@
             const std::string &offset,
             bool isReader) const override;
 
+    void emitHidlDefinition(Formatter& out) const override;
     void emitTypeDeclarations(Formatter& out) const override;
     void emitTypeForwardDeclaration(Formatter& out) const override;
     void emitGlobalTypeDeclarations(Formatter& out) const override;
diff --git a/FmqType.cpp b/FmqType.cpp
index f5eaaa6..ed323f2 100644
--- a/FmqType.cpp
+++ b/FmqType.cpp
@@ -23,8 +23,8 @@
 
 namespace android {
 
-FmqType::FmqType(const char* nsp, const char* name, Scope* parent)
-    : TemplatedType(parent), mNamespace(nsp), mName(name) {}
+FmqType::FmqType(const char* nsp, const char* name, Scope* parent, const char* definedName)
+    : TemplatedType(parent, definedName), mNamespace(nsp), mName(name) {}
 
 std::string FmqType::templatedTypeName() const {
     return mName;
diff --git a/FmqType.h b/FmqType.h
index 747b10a..f6b1f18 100644
--- a/FmqType.h
+++ b/FmqType.h
@@ -23,7 +23,7 @@
 namespace android {
 
 struct FmqType : public TemplatedType {
-    FmqType(const char* nsp, const char* name, Scope* parent);
+    FmqType(const char* nsp, const char* name, Scope* parent, const char* definedName);
 
     std::string fullName() const;
 
diff --git a/FormattingConstants.h b/FormattingConstants.h
new file mode 100644
index 0000000..1ab5a17
--- /dev/null
+++ b/FormattingConstants.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 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 <cstddef>
+
+namespace android {
+constexpr size_t MAX_LINE_LENGTH = 100;
+};
\ No newline at end of file
diff --git a/HandleType.cpp b/HandleType.cpp
index 56660a6..1c612e5 100644
--- a/HandleType.cpp
+++ b/HandleType.cpp
@@ -23,7 +23,7 @@
 
 namespace android {
 
-HandleType::HandleType(Scope* parent) : Type(parent) {}
+HandleType::HandleType(Scope* parent) : Type(parent, "handle") {}
 
 bool HandleType::isHandle() const {
     return true;
diff --git a/Interface.cpp b/Interface.cpp
index b6d2f38..ff33972 100644
--- a/Interface.cpp
+++ b/Interface.cpp
@@ -822,6 +822,31 @@
     }
 }
 
+void Interface::emitHidlDefinition(Formatter& out) const {
+    if (getDocComment() != nullptr) getDocComment()->emit(out);
+    out << typeName() << " ";
+
+    const Interface* super = superType();
+    if (super != nullptr && !super->isIBase()) {
+        out << "extends " << super->fqName().getRelativeFQName(fqName()) << " ";
+    }
+
+    out << "{\n";
+
+    out.indent([&] {
+        const std::vector<const NamedType*>& definedTypes = getSortedDefinedTypes();
+        out.join(definedTypes.begin(), definedTypes.end(), "\n",
+                 [&](auto t) { t->emitHidlDefinition(out); });
+
+        if (definedTypes.size() > 0 && userDefinedMethods().size() > 0) out << "\n";
+
+        out.join(userDefinedMethods().begin(), userDefinedMethods().end(), "\n",
+                 [&](auto method) { method->emitHidlDefinition(out); });
+    });
+
+    out << "};\n";
+}
+
 void Interface::emitPackageTypeDeclarations(Formatter& out) const {
     Scope::emitPackageTypeDeclarations(out);
 
diff --git a/Interface.h b/Interface.h
index 56570f5..e0fcb8b 100644
--- a/Interface.h
+++ b/Interface.h
@@ -111,6 +111,8 @@
             bool isReader,
             ErrorMode mode) const override;
 
+    void emitHidlDefinition(Formatter& out) const override;
+
     void emitPackageTypeDeclarations(Formatter& out) const override;
     void emitPackageTypeHeaderDefinitions(Formatter& out) const override;
     void emitTypeDefinitions(Formatter& out, const std::string& prefix) const override;
diff --git a/MemoryType.cpp b/MemoryType.cpp
index 444ba92..380214f 100644
--- a/MemoryType.cpp
+++ b/MemoryType.cpp
@@ -23,7 +23,7 @@
 
 namespace android {
 
-MemoryType::MemoryType(Scope* parent) : Type(parent) {}
+MemoryType::MemoryType(Scope* parent) : Type(parent, "memory") {}
 
 std::string MemoryType::getCppType(StorageMode mode,
                                    bool specifyNamespaces) const {
diff --git a/Method.cpp b/Method.cpp
index 7954462..42b4d5c 100644
--- a/Method.cpp
+++ b/Method.cpp
@@ -18,12 +18,15 @@
 
 #include "Annotation.h"
 #include "ConstantExpression.h"
+#include "FormattingConstants.h"
 #include "ScalarType.h"
 #include "Type.h"
 
 #include <android-base/logging.h>
+#include <hidl-util/FQName.h>
 #include <hidl-util/Formatter.h>
 #include <algorithm>
+#include <vector>
 
 namespace android {
 
@@ -263,6 +266,53 @@
     out << ")";
 }
 
+static void fillHidlArgResultTokens(const std::vector<NamedReference<Type>*>& args,
+                                    WrappedOutput* wrappedOutput) {
+    for (auto iter = args.begin(); iter != args.end(); ++iter) {
+        auto arg = *iter;
+        std::string out = arg->localName() + " " + arg->name();
+        if (iter != args.begin()) {
+            *wrappedOutput << ",";
+            wrappedOutput->group([&] {
+                wrappedOutput->printUnlessWrapped(" ");
+                *wrappedOutput << out;
+            });
+        } else {
+            wrappedOutput->group([&] { *wrappedOutput << out; });
+        }
+    }
+}
+
+void Method::emitHidlDefinition(Formatter& out) const {
+    if (getDocComment() != nullptr) getDocComment()->emit(out);
+
+    out.join(mAnnotations->begin(), mAnnotations->end(), "\n",
+             [&](auto annotation) { annotation->dump(out); });
+    if (!mAnnotations->empty()) out << "\n";
+
+    WrappedOutput wrappedOutput(MAX_LINE_LENGTH);
+
+    if (isOneway()) wrappedOutput << "oneway ";
+    wrappedOutput << name() << "(";
+
+    wrappedOutput.group([&] { fillHidlArgResultTokens(args(), &wrappedOutput); });
+
+    wrappedOutput << ")";
+
+    if (!results().empty()) {
+        wrappedOutput.group([&] {
+            wrappedOutput.printUnlessWrapped(" ");
+            wrappedOutput << "generates (";
+            fillHidlArgResultTokens(results(), &wrappedOutput);
+            wrappedOutput << ")";
+        });
+    }
+
+    wrappedOutput << ";\n";
+
+    out << wrappedOutput;
+}
+
 bool Method::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
     if (!std::all_of(mArgs->begin(), mArgs->end(),
                      [&](const auto* arg) { return (*arg)->isJavaCompatible(visited); })) {
diff --git a/Method.h b/Method.h
index 4b43a4b..5c5ac47 100644
--- a/Method.h
+++ b/Method.h
@@ -103,6 +103,8 @@
     void emitJavaResultSignature(Formatter &out) const;
     void emitJavaSignature(Formatter& out) const;
 
+    void emitHidlDefinition(Formatter& out) const;
+
     const NamedReference<Type>* canElideCallback() const;
 
     bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const;
diff --git a/NamedType.cpp b/NamedType.cpp
index 645c63d..dba9248 100644
--- a/NamedType.cpp
+++ b/NamedType.cpp
@@ -20,7 +20,7 @@
 
 NamedType::NamedType(const char* localName, const FQName& fullName, const Location& loc,
                      Scope* parent)
-    : Type(parent), mLocalName(localName), mFullName(fullName), mLocation(loc) {}
+    : Type(parent, localName), mLocalName(localName), mFullName(fullName), mLocation(loc) {}
 
 bool NamedType::isNamedType() const {
     return true;
@@ -54,4 +54,3 @@
 }
 
 }  // namespace android
-
diff --git a/PointerType.cpp b/PointerType.cpp
index 2b8e5a4..f6eaf54 100644
--- a/PointerType.cpp
+++ b/PointerType.cpp
@@ -21,7 +21,7 @@
 
 namespace android {
 
-PointerType::PointerType(Scope* parent) : Type(parent) {}
+PointerType::PointerType(Scope* parent) : Type(parent, "pointer") {}
 
 bool PointerType::isPointer() const {
     return true;
diff --git a/Reference.h b/Reference.h
index ba39ac4..b817514 100644
--- a/Reference.h
+++ b/Reference.h
@@ -21,6 +21,8 @@
 #include <android-base/logging.h>
 #include <hidl-util/FQName.h>
 
+#include <string>
+
 #include "DocComment.h"
 #include "Location.h"
 
@@ -34,20 +36,27 @@
     Reference() = default;
     virtual ~Reference() {}
 
-    Reference(const FQName& fqName, const Location& location)
-        : mResolved(nullptr), mFqName(fqName), mLocation(location) {}
+    Reference(const std::string& localName, const FQName& fqName, const Location& location)
+        : mResolved(nullptr), mFqName(fqName), mLocation(location), mLocalName(localName) {}
 
-    Reference(T* type, const Location& location) : mResolved(type), mLocation(location) {
+    Reference(const std::string& localName, T* type, const Location& location)
+        : mResolved(type), mLocation(location), mLocalName(localName) {
         CHECK(type != nullptr);
     }
 
     template <class OtherT>
     Reference(const Reference<OtherT>& ref)
-        : mResolved(ref.mResolved), mFqName(ref.mFqName), mLocation(ref.mLocation) {}
+        : mResolved(ref.mResolved),
+          mFqName(ref.mFqName),
+          mLocation(ref.mLocation),
+          mLocalName(ref.mLocalName) {}
 
     template <class OtherT>
     Reference(const Reference<OtherT>& ref, const Location& location)
-        : mResolved(ref.mResolved), mFqName(ref.mFqName), mLocation(location) {}
+        : mResolved(ref.mResolved),
+          mFqName(ref.mFqName),
+          mLocation(location),
+          mLocalName(ref.mLocalName) {}
 
     /* Returns true iff referred type is resolved
        Referred type's field might be not resolved */
@@ -100,7 +109,9 @@
         return mLocation;
     }
 
-   private:
+    const std::string& localName() const { return mLocalName; }
+
+  private:
     /* Referred type */
     T* mResolved = nullptr;
     /* Reference name for lookup */
@@ -109,6 +120,9 @@
        and handling forward reference restrictions */
     Location mLocation;
 
+    /* Name used in the .hal file */
+    std::string mLocalName;
+
     bool hasLookupFqName() const {
         // Valid only while not resolved to prevent confusion when
         // ref.hasLookupFqName() is false while ref,get()->fqName is valid.
diff --git a/ScalarType.cpp b/ScalarType.cpp
index e6f3fe4..142aac2 100644
--- a/ScalarType.cpp
+++ b/ScalarType.cpp
@@ -20,7 +20,12 @@
 
 namespace android {
 
-ScalarType::ScalarType(Kind kind, Scope* parent) : Type(parent), mKind(kind) {}
+static const char* const hidlIdentifiers[] = {"bool",     "int8_t",  "uint8_t",  "int16_t",
+                                              "uint16_t", "int32_t", "uint32_t", "int64_t",
+                                              "uint64_t", "float",   "double"};
+
+ScalarType::ScalarType(Kind kind, Scope* parent)
+    : Type(parent, hidlIdentifiers[kind]), mKind(kind) {}
 
 const ScalarType *ScalarType::resolveToScalarType() const {
     return this;
diff --git a/Scope.cpp b/Scope.cpp
index d4953aa..cfe004d 100644
--- a/Scope.cpp
+++ b/Scope.cpp
@@ -122,6 +122,16 @@
     return ret;
 }
 
+std::vector<const NamedType*> Scope::getSortedDefinedTypes() const {
+    std::vector<const NamedType*> ret;
+    ret.insert(ret.end(), mTypes.begin(), mTypes.end());
+
+    std::sort(ret.begin(), ret.end(), [](const NamedType* lhs, const NamedType* rhs) -> bool {
+        return lhs->location() < rhs->location();
+    });
+    return ret;
+}
+
 std::vector<const ConstantExpression*> Scope::getConstantExpressions() const {
     std::vector<const ConstantExpression*> ret;
     for (const auto* annotation : mAnnotations) {
@@ -146,6 +156,12 @@
     }
 }
 
+void Scope::emitHidlDefinition(Formatter& out) const {
+    const std::vector<const NamedType*>& definedTypes = getSortedDefinedTypes();
+    out.join(definedTypes.begin(), definedTypes.end(), "\n",
+             [&](auto t) { t->emitHidlDefinition(out); });
+}
+
 void Scope::emitTypeDeclarations(Formatter& out) const {
     if (mTypes.empty()) return;
 
diff --git a/Scope.h b/Scope.h
index bf7d1cd..a62bad8 100644
--- a/Scope.h
+++ b/Scope.h
@@ -69,6 +69,8 @@
     void emitPackageTypeHeaderDefinitions(Formatter& out) const override;
     void emitPackageHwDeclarations(Formatter& out) const override;
 
+    void emitHidlDefinition(Formatter& out) const override;
+
     void emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const override;
 
     void emitTypeDefinitions(Formatter& out, const std::string& prefix) const override;
@@ -82,7 +84,10 @@
     void appendToExportedTypesVector(
             std::vector<const Type *> *exportedTypes) const override;
 
-   private:
+  protected:
+    std::vector<const NamedType*> getSortedDefinedTypes() const;
+
+  private:
     std::vector<NamedType *> mTypes;
     std::map<std::string, size_t> mTypeIndexByName;
     std::vector<Annotation*> mAnnotations;
diff --git a/StringType.cpp b/StringType.cpp
index b4f72be..edb183f 100644
--- a/StringType.cpp
+++ b/StringType.cpp
@@ -22,7 +22,7 @@
 
 namespace android {
 
-StringType::StringType(Scope* parent) : Type(parent) {}
+StringType::StringType(Scope* parent) : Type(parent, "string") {}
 
 bool StringType::isString() const {
     return true;
diff --git a/Type.cpp b/Type.cpp
index d576615..80bce2a 100644
--- a/Type.cpp
+++ b/Type.cpp
@@ -25,10 +25,12 @@
 #include <hidl-util/Formatter.h>
 #include <algorithm>
 #include <iostream>
+#include <string>
 
 namespace android {
 
-Type::Type(Scope* parent) : mParent(parent) {}
+Type::Type(Scope* parent, const std::string& definedName)
+    : mDefinedName(definedName), mParent(parent) {}
 
 Type::~Type() {}
 
@@ -369,6 +371,10 @@
     return mParent;
 }
 
+const std::string& Type::definedName() const {
+    return mDefinedName;
+}
+
 std::string Type::getCppType(StorageMode, bool) const {
     CHECK(!"Should not be here") << typeName();
     return std::string();
@@ -597,6 +603,10 @@
     handleError(out, mode);
 }
 
+void Type::emitHidlDefinition(Formatter&) const {
+    CHECK(!"Should not be here.") << typeName();
+}
+
 void Type::emitTypeDeclarations(Formatter&) const {}
 
 void Type::emitTypeForwardDeclaration(Formatter&) const {}
@@ -725,7 +735,8 @@
 
 ////////////////////////////////////////
 
-TemplatedType::TemplatedType(Scope* parent) : Type(parent) {}
+TemplatedType::TemplatedType(Scope* parent, const std::string& definedName)
+    : Type(parent, definedName) {}
 
 std::string TemplatedType::typeName() const {
     return templatedTypeName() + " of " + mElementType->typeName();
@@ -737,6 +748,7 @@
     CHECK(!elementType.isEmptyReference());
 
     mElementType = elementType;
+    mDefinedName = mDefinedName + "<" + mElementType.localName() + ">";
 }
 
 const Type* TemplatedType::getElementType() const {
diff --git a/Type.h b/Type.h
index 6131068..0ed120b 100644
--- a/Type.h
+++ b/Type.h
@@ -38,7 +38,7 @@
 struct Scope;
 
 struct Type : DocCommentable {
-    Type(Scope* parent);
+    Type(Scope* parent, const std::string& definedName);
     virtual ~Type();
 
     virtual bool isArray() const;
@@ -147,6 +147,8 @@
     Scope* parent();
     const Scope* parent() const;
 
+    const std::string& definedName() const;
+
     enum StorageMode {
         StorageMode_Stack,
         StorageMode_Argument,
@@ -251,6 +253,8 @@
             const std::string &offset,
             bool isReader) const;
 
+    virtual void emitHidlDefinition(Formatter& out) const;
+
     virtual void emitTypeDeclarations(Formatter& out) const;
 
     virtual void emitGlobalTypeDeclarations(Formatter& out) const;
@@ -344,7 +348,10 @@
             const std::string &methodName,
             const std::string &name) const;
 
-   private:
+    // This is the name given to the type in the hidl file
+    std::string mDefinedName;
+
+  private:
     ParseStage mParseStage = ParseStage::PARSE;
     Scope* const mParent;
 
@@ -370,8 +377,8 @@
     void emitVtsAttributeType(Formatter& out) const override;
 
    protected:
-    TemplatedType(Scope* parent);
-    Reference<Type> mElementType;
+     TemplatedType(Scope* parent, const std::string& definedName);
+     Reference<Type> mElementType;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(TemplatedType);
diff --git a/TypeDef.cpp b/TypeDef.cpp
index 80522a2..2e36c28 100644
--- a/TypeDef.cpp
+++ b/TypeDef.cpp
@@ -80,5 +80,9 @@
         << ";\n\n";
 }
 
+void TypeDef::emitHidlDefinition(Formatter& out) const {
+    out << "typedef " << mReferencedType.localName() << " " << localName() << ";\n";
+}
+
 }  // namespace android
 
diff --git a/TypeDef.h b/TypeDef.h
index 5ebba3e..d620765 100644
--- a/TypeDef.h
+++ b/TypeDef.h
@@ -44,8 +44,9 @@
     std::vector<const Reference<Type>*> getReferences() const override;
 
     void emitTypeDeclarations(Formatter& out) const override;
+    void emitHidlDefinition(Formatter& out) const override;
 
-   private:
+  private:
     Reference<Type> mReferencedType;
 
     DISALLOW_COPY_AND_ASSIGN(TypeDef);
diff --git a/VectorType.cpp b/VectorType.cpp
index b5e8e22..3bd2d38 100644
--- a/VectorType.cpp
+++ b/VectorType.cpp
@@ -25,7 +25,7 @@
 
 namespace android {
 
-VectorType::VectorType(Scope* parent) : TemplatedType(parent) {}
+VectorType::VectorType(Scope* parent) : TemplatedType(parent, "vec") {}
 
 std::string VectorType::templatedTypeName() const {
     return "vector";
diff --git a/generateFormattedHidl.cpp b/generateFormattedHidl.cpp
new file mode 100644
index 0000000..742652e
--- /dev/null
+++ b/generateFormattedHidl.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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 <hidl-util/FQName.h>
+#include <hidl-util/Formatter.h>
+
+#include "AST.h"
+
+namespace android {
+
+void AST::generateFormattedHidl(Formatter& out) const {
+    if (mHeader != nullptr) {
+        mHeader->emit(out, CommentType::MULTILINE);
+        out << "\n";
+    }
+
+    out << "package " << mPackage.string() << ";\n\n";
+
+    out.join(mImportStatements.begin(), mImportStatements.end(), "\n", [&](const auto& import) {
+        if (import.fqName.name().empty()) {
+            out << "import " << import.fqName.string() << ";";
+        } else {
+            out << "import " << import.fqName.getRelativeFQName(mPackage) << ";";
+        }
+    });
+    if (!mImportStatements.empty()) out << "\n\n";
+
+    mRootScope.emitHidlDefinition(out);
+}
+
+}  // namespace android
diff --git a/hidl-gen_l.ll b/hidl-gen_l.ll
index 81fe151..7d76832 100644
--- a/hidl-gen_l.ll
+++ b/hidl-gen_l.ll
@@ -129,8 +129,8 @@
 "pointer"           { yylval->type = new PointerType(*scope); return token::TYPE; }
 "string"            { yylval->type = new StringType(*scope); return token::TYPE; }
 
-"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; }
+"fmq_sync"          { yylval->type = new FmqType("::android::hardware", "MQDescriptorSync", *scope, "fmq_sync"); return token::TEMPLATED; }
+"fmq_unsync"        { yylval->type = new FmqType("::android::hardware", "MQDescriptorUnsync", *scope, "fmq_unsync"); return token::TEMPLATED; }
 
 "("                 { return('('); }
 ")"                 { return(')'); }
diff --git a/hidl-gen_y.yy b/hidl-gen_y.yy
index 58f78d0..7d4650d 100644
--- a/hidl-gen_y.yy
+++ b/hidl-gen_y.yy
@@ -529,11 +529,11 @@
 fqtype
     : fqname
       {
-          $$ = new Reference<Type>(*$1, convertYYLoc(@1, ast));
+          $$ = new Reference<Type>($1->string(), *$1, convertYYLoc(@1, ast));
       }
     | TYPE
       {
-          $$ = new Reference<Type>($1, convertYYLoc(@1, ast));
+          $$ = new Reference<Type>($1->definedName(), $1, convertYYLoc(@1, ast));
       }
     ;
 
@@ -713,7 +713,7 @@
               }
 
               if (superType == nullptr) {
-                  superType = new Reference<Type>(gIBaseFqName, convertYYLoc(@$, ast));
+                  superType = new Reference<Type>(gIBaseFqName.string(), gIBaseFqName, convertYYLoc(@$, ast));
               }
           }
 
@@ -788,12 +788,12 @@
           }
 
           $$ = new ReferenceConstantExpression(
-              Reference<LocalIdentifier>(*$1, convertYYLoc(@1, ast)), $1->string());
+              Reference<LocalIdentifier>($1->string(), *$1, convertYYLoc(@1, ast)), $1->string());
       }
     | fqname '#' IDENTIFIER
       {
           $$ = new AttributeConstantExpression(
-              Reference<Type>(*$1, convertYYLoc(@1, ast)), $1->string(), $3);
+              Reference<Type>($1->string(), *$1, convertYYLoc(@1, ast)), $1->string(), $3);
       }
     | const_expr '?' const_expr ':' const_expr
       {
@@ -1045,8 +1045,8 @@
               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, ast));
+              ScalarType* scalar = new ScalarType(ScalarType::KIND_INT64, *scope);
+              storageType = new Reference<Type>(scalar->definedName(), scalar, convertYYLoc(@2, ast));
           }
 
           EnumType* enumType = new EnumType(
@@ -1123,13 +1123,13 @@
     | TEMPLATED '<' type '>'
       {
           $1->setElementType(*$3);
-          $$ = new Reference<Type>($1, convertYYLoc(@1, ast));
+          $$ = new Reference<Type>($1->definedName(), $1, convertYYLoc(@1, ast));
       }
     | TEMPLATED '<' TEMPLATED '<' type RSHIFT
       {
           $3->setElementType(*$5);
-          $1->setElementType(Reference<Type>($3, convertYYLoc(@3, ast)));
-          $$ = new Reference<Type>($1, convertYYLoc(@1, ast));
+          $1->setElementType(Reference<Type>($3->definedName(), $3, convertYYLoc(@3, ast)));
+          $$ = new Reference<Type>($1->definedName(), $1, convertYYLoc(@1, ast));
       }
     ;
 
@@ -1149,12 +1149,12 @@
     : array_type_base ignore_doc_comments { $$ = $1; }
     | array_type ignore_doc_comments
       {
-        $$ = new Reference<Type>($1, convertYYLoc(@1, ast));
+        $$ = new Reference<Type>($1->definedName(), $1, convertYYLoc(@1, ast));
       }
     | INTERFACE ignore_doc_comments
       {
         // "interface" is a synonym of android.hidl.base@1.0::IBase
-        $$ = new Reference<Type>(gIBaseFqName, convertYYLoc(@1, ast));
+        $$ = new Reference<Type>("interface", gIBaseFqName, convertYYLoc(@1, ast));
       }
     ;
 
@@ -1162,7 +1162,7 @@
     : type { $$ = $1; }
     | annotated_compound_declaration ignore_doc_comments
       {
-          $$ = new Reference<Type>($1, convertYYLoc(@1, ast));
+          $$ = new Reference<Type>($1->definedName(), $1, convertYYLoc(@1, ast));
       }
     ;
 
diff --git a/host_utils/Formatter.cpp b/host_utils/Formatter.cpp
index 534e8cd..2268e30 100644
--- a/host_utils/Formatter.cpp
+++ b/host_utils/Formatter.cpp
@@ -19,6 +19,7 @@
 #include <assert.h>
 
 #include <android-base/logging.h>
+#include <vector>
 
 namespace android {
 
@@ -123,7 +124,7 @@
 
         if (pos == std::string::npos) {
             if (mAtStartOfLine) {
-                fprintf(mFile, "%*s", (int)(mSpacesPerIndent * mIndentDepth), "");
+                fprintf(mFile, "%*s", (int)(getIndentation()), "");
                 fprintf(mFile, "%s", mLinePrefix.c_str());
                 mAtStartOfLine = false;
             }
@@ -133,7 +134,7 @@
         }
 
         if (mAtStartOfLine && (pos > start || !mLinePrefix.empty())) {
-            fprintf(mFile, "%*s", (int)(mSpacesPerIndent * mIndentDepth), "");
+            fprintf(mFile, "%*s", (int)(getIndentation()), "");
             fprintf(mFile, "%s", mLinePrefix.c_str());
         }
 
@@ -151,6 +152,56 @@
     return *this;
 }
 
+Formatter& Formatter::operator<<(const WrappedOutput& wrappedOutput) {
+    CHECK(mAtStartOfLine) << "This function should only be called at the start of a new line";
+
+    size_t currentPosition = getIndentation();
+    std::function<void(Formatter&, const WrappedOutput::Block&)> printBlock =
+            [&](Formatter& out, const WrappedOutput::Block& block) {
+                size_t blockSize = block.computeSize(false);
+                if (blockSize + currentPosition < wrappedOutput.mLineLength) {
+                    block.print(out, false);
+                    currentPosition += blockSize;
+                    return;
+                }
+
+                // Everything will not fit on this line. Try to fit it on the next line.
+                blockSize = block.computeSize(true);
+                if (blockSize + getIndentation() + mSpacesPerIndent < wrappedOutput.mLineLength) {
+                    out << "\n";
+                    out.indent();
+
+                    block.print(out, true);
+                    currentPosition = getIndentation() + blockSize;
+
+                    out.unindent();
+                    return;
+                }
+
+                if (!block.content.empty()) {
+                    // Doesn't have subblocks. This means that the block itself is too big.
+                    // Have to print it out.
+                    out << "\n";
+                    out.indent();
+
+                    block.print(out, true);
+                    currentPosition = getIndentation() + blockSize;
+
+                    out.unindent();
+                    return;
+                }
+
+                // Everything will not fit on this line. Go through all the children
+                for (const WrappedOutput::Block& subBlock : block.blocks) {
+                    printBlock(out, subBlock);
+                }
+            };
+
+    printBlock(*this, wrappedOutput.mRootBlock);
+
+    return *this;
+}
+
 // NOLINT to suppress missing parentheses warning about __type__.
 #define FORMATTER_INPUT_INTEGER(__type__)                       \
     Formatter& Formatter::operator<<(__type__ n) { /* NOLINT */ \
@@ -187,10 +238,101 @@
     return mFile != nullptr;
 }
 
+size_t Formatter::getIndentation() const {
+    return mSpacesPerIndent * mIndentDepth;
+}
+
 void Formatter::output(const std::string &text) const {
     CHECK(isValid());
 
     fprintf(mFile, "%s", text.c_str());
 }
 
+WrappedOutput::Block::Block(const std::string& content, Block* const parent)
+    : content(content), parent(parent) {}
+
+size_t WrappedOutput::Block::computeSize(bool wrapped) const {
+    CHECK(content.empty() || blocks.empty());
+
+    // There is a wrap, so the block would not be printed
+    if (printUnlessWrapped && wrapped) return 0;
+
+    size_t size = content.size();
+    for (auto block = blocks.begin(); block != blocks.end(); ++block) {
+        if (block == blocks.begin()) {
+            // Only the first one can be wrapped (since content.empty())
+            size += block->computeSize(wrapped);
+        } else {
+            size += block->computeSize(false);
+        }
+    }
+
+    return size;
+}
+
+void WrappedOutput::Block::print(Formatter& out, bool wrapped) const {
+    CHECK(content.empty() || blocks.empty());
+
+    // There is a wrap, so the block should not be printed
+    if (printUnlessWrapped && wrapped) return;
+
+    out << content;
+    for (auto block = blocks.begin(); block != blocks.end(); ++block) {
+        if (block == blocks.begin()) {
+            // Only the first one can be wrapped (since content.empty())
+            block->print(out, wrapped);
+        } else {
+            block->print(out, false);
+        }
+    }
+}
+
+WrappedOutput::WrappedOutput(size_t lineLength)
+    : mLineLength(lineLength), mRootBlock(Block("", nullptr)) {
+    mCurrentBlock = &mRootBlock;
+}
+
+WrappedOutput& WrappedOutput::operator<<(const std::string& str) {
+    std::vector<Block>& blockVec = mCurrentBlock->blocks;
+    if (!blockVec.empty()) {
+        Block& last = blockVec.back();
+        if (!last.populated && last.blocks.empty()) {
+            last.content += str;
+
+            return *this;
+        }
+    }
+
+    blockVec.emplace_back(str, mCurrentBlock);
+    return *this;
+}
+
+WrappedOutput& WrappedOutput::printUnlessWrapped(const std::string& str) {
+    std::vector<Block>& blockVec = mCurrentBlock->blocks;
+    if (!blockVec.empty()) {
+        blockVec.back().populated = true;
+    }
+
+    blockVec.emplace_back(str, mCurrentBlock);
+    blockVec.back().populated = true;
+    blockVec.back().printUnlessWrapped = true;
+
+    return *this;
+}
+
+void WrappedOutput::group(const std::function<void(void)>& block) {
+    std::vector<Block>& blockVec = mCurrentBlock->blocks;
+    if (!blockVec.empty()) {
+        blockVec.back().populated = true;
+    }
+
+    blockVec.emplace_back("", mCurrentBlock);
+    mCurrentBlock = &blockVec.back();
+
+    block();
+
+    mCurrentBlock->populated = true;
+    mCurrentBlock = mCurrentBlock->parent;
+}
+
 }  // namespace android
diff --git a/host_utils/include/hidl-util/Formatter.h b/host_utils/include/hidl-util/Formatter.h
index bdb89be..5ce2351 100644
--- a/host_utils/include/hidl-util/Formatter.h
+++ b/host_utils/include/hidl-util/Formatter.h
@@ -20,9 +20,47 @@
 
 #include <functional>
 #include <string>
+#include <vector>
 
 namespace android {
 
+struct Formatter;
+
+struct WrappedOutput {
+    WrappedOutput(size_t lineLength);
+
+    void group(const std::function<void(void)>& block);
+    WrappedOutput& operator<<(const std::string& str);
+    WrappedOutput& printUnlessWrapped(const std::string& str);
+
+  private:
+    struct Block {
+        Block(const std::string& content, Block* const parent);
+
+        // populated helps indicate if we are done filling up the Block.
+        // this allows WrappedOutput to keep adding content to this block
+        // till it is determined that it is full.
+        bool populated = false;
+        bool printUnlessWrapped = false;
+
+        // Only one of content or blocks can have content.
+        std::string content;
+        std::vector<Block> blocks;
+
+        Block* const parent;
+
+        size_t computeSize(bool wrapped) const;
+        void print(Formatter& out, bool wrapped) const;
+    };
+
+    size_t mLineLength;
+
+    Block mRootBlock;
+    Block* mCurrentBlock;
+
+    friend struct Formatter;
+};
+
 // Two styles to use a Formatter.
 // One is with .indent() calls and operator<<.
 //     out << "if (good) {\n"; out.indent(); out << "blah\nblah\n"; out.unindent(); out << "}\n";
@@ -128,6 +166,9 @@
     Formatter &operator<<(double c);
     Formatter &operator<<(long double c);
 
+    // This assumes that the formatter is currently on a newline
+    Formatter& operator<<(const WrappedOutput& wrappedOutput);
+
     // Puts a prefix before each line. This is useful if
     // you want to start a // comment block, for example.
     // The prefix will be put before the indentation.
@@ -137,8 +178,9 @@
     void unsetLinePrefix();
 
     bool isValid() const;
+    size_t getIndentation() const;
 
-   private:
+  private:
     // Creates an invalid formatter object.
     Formatter();
 
diff --git a/main.cpp b/main.cpp
index 27151d6..2a13fff 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1283,6 +1283,21 @@
             },
         },
     },
+    {
+        "format",
+        "Reformats the .hal files",
+        OutputMode::NEEDS_SRC,
+        Coordinator::Location::PACKAGE_ROOT,
+        GenerationGranularity::PER_FILE,
+        validateForSource,
+        {
+            {
+                FileGenerator::alwaysGenerate,
+                [](const FQName& fqName) { return fqName.name() + ".hal"; },
+                astGenerationFunction(&AST::generateFormattedHidl),
+            },
+        }
+    },
 };
 // clang-format on
 
diff --git a/utils/FQName.cpp b/utils/FQName.cpp
index efa835a..dd191c6 100644
--- a/utils/FQName.cpp
+++ b/utils/FQName.cpp
@@ -160,6 +160,31 @@
     return !invalid;
 }
 
+std::string FQName::getRelativeFQName(const FQName& relativeTo) const {
+    if (relativeTo.mPackage != mPackage) {
+        return string();
+    }
+
+    // Package is the same
+    std::string out;
+    if (relativeTo.version() != version()) {
+        out.append(atVersion());
+        if (!mName.empty() && !version().empty()) {
+            out.append("::");
+        }
+    }
+
+    if (!mName.empty()) {
+        out.append(mName);
+        if (!mValueName.empty()) {
+            out.append(":");
+            out.append(mValueName);
+        }
+    }
+
+    return out;
+}
+
 const std::string& FQName::package() const {
     return mPackage;
 }
diff --git a/utils/include/hidl-util/FQName.h b/utils/include/hidl-util/FQName.h
index 416340a..9d90302 100644
--- a/utils/include/hidl-util/FQName.h
+++ b/utils/include/hidl-util/FQName.h
@@ -108,6 +108,13 @@
     bool operator==(const FQName &other) const;
     bool operator!=(const FQName &other) const;
 
+    // Provides the FQName relative to "relativeTo"
+    // If this is android.hardware.foo@1.0::IFoo it returns
+    // relativeTo: android.hardware.foo@1.0::IBar - IFoo
+    // relativeTo: android.hardware.foo@1.2::IFoo - @1.0::IFoo
+    // relativeTo: android.hardware.bar@1.0::IFoo - android.hardware.foo@1.0::IFoo
+    std::string getRelativeFQName(const FQName& relativeTo) const;
+
     // Must be called on an interface
     // android.hardware.foo@1.0::IBar
     // -> Bar