Allow clean mode in assemble_vintf

XmlConverter serialization accepts optional flags that controls
the sections in final output.

Test: assemble_vintf -i current.xml --no-hals and --hals-only
Test: assemble_vintf -i device/*/manifest.xml --no-hals and --hals-only

Bug: 62720090
Change-Id: Icfc1bdf28f3bc550a75f25e17fca742522aee24c
Merged-In: Icfc1bdf28f3bc550a75f25e17fca742522aee24c
diff --git a/assemble_vintf.cpp b/assemble_vintf.cpp
index 8874f69..d031d83 100644
--- a/assemble_vintf.cpp
+++ b/assemble_vintf.cpp
@@ -199,9 +199,9 @@
                      "    Many entries other than HALs are zero-filled and\n"
                      "    require human attention. \n"
                      "-->\n"
-                  << gCompatibilityMatrixConverter(generatedMatrix);
+                  << gCompatibilityMatrixConverter(generatedMatrix, mSerializeFlags);
         } else {
-            out() << gHalManifestConverter(*halManifest);
+            out() << gHalManifestConverter(*halManifest, mSerializeFlags);
         }
         out().flush();
 
@@ -274,7 +274,7 @@
             }
             matrix->framework.mAvbMetaVersion = avbMetaVersion;
         }
-        out() << gCompatibilityMatrixConverter(*matrix);
+        out() << gCompatibilityMatrixConverter(*matrix, mSerializeFlags);
         out().flush();
 
         if (mCheckFile.is_open()) {
@@ -385,6 +385,18 @@
 
     void setOutputMatrix() { mOutputMatrix = true; }
 
+    bool setHalsOnly() {
+        if (mSerializeFlags) return false;
+        mSerializeFlags |= SerializeFlag::HALS_ONLY;
+        return true;
+    }
+
+    bool setNoHals() {
+        if (mSerializeFlags) return false;
+        mSerializeFlags |= SerializeFlag::NO_HALS;
+        return true;
+    }
+
     bool addKernel(const std::string& kernelArg) {
         auto ind = kernelArg.find(':');
         if (ind == std::string::npos) {
@@ -412,6 +424,7 @@
     std::unique_ptr<std::ofstream> mOutFileRef;
     std::ifstream mCheckFile;
     bool mOutputMatrix = false;
+    SerializeFlags mSerializeFlags = SerializeFlag::EVERYTHING;
     std::map<Version, std::string> mKernels;
 };
 
@@ -451,17 +464,25 @@
                  "               <version> has format: 3.18\n"
                  "               <android-base.cfg> is the location of android-base.cfg\n"
                  "               <android-base-arch.cfg> is the location of an optional\n"
-                 "               arch-specific config fragment, more than one may be specified\n";
+                 "               arch-specific config fragment, more than one may be specified\n"
+                 "    -l, --hals-only\n"
+                 "               Output has only <hal> entries. Cannot be used with -n.\n"
+                 "    -n, --no-hals\n"
+                 "               Output has no <hal> entries (but all other entries).\n"
+                 "               Cannot be used with -l.\n";
 }
 
 int main(int argc, char **argv) {
-    const struct option longopts[] = {{"kernel", required_argument, NULL, 'k'}, {0, 0, 0, 0}};
+    const struct option longopts[] = {{"kernel", required_argument, NULL, 'k'},
+                                      {"hals-only", no_argument, NULL, 'l'},
+                                      {"no-hals", no_argument, NULL, 'n'},
+                                      {0, 0, 0, 0}};
 
     std::string outFilePath;
     ::android::vintf::AssembleVintf assembleVintf;
     int res;
     int optind;
-    while ((res = getopt_long(argc, argv, "hi:o:mc:", longopts, &optind)) >= 0) {
+    while ((res = getopt_long(argc, argv, "hi:o:mc:nl", longopts, &optind)) >= 0) {
         switch (res) {
             case 'i': {
                 char* inFilePath = strtok(optarg, ":");
@@ -505,6 +526,18 @@
                 }
             } break;
 
+            case 'l': {
+                if (!assembleVintf.setHalsOnly()) {
+                    return 1;
+                }
+            } break;
+
+            case 'n': {
+                if (!assembleVintf.setNoHals()) {
+                    return 1;
+                }
+            } break;
+
             case 'h':
             default: {
                 help();
diff --git a/include/vintf/parse_xml.h b/include/vintf/parse_xml.h
index f1c6a98..61c4f8e 100644
--- a/include/vintf/parse_xml.h
+++ b/include/vintf/parse_xml.h
@@ -23,14 +23,27 @@
 namespace android {
 namespace vintf {
 
+enum SerializeFlag : uint32_t {
+    NO_HALS = 1 << 0,
+    NO_AVB = 1 << 1,
+    NO_SEPOLICY = 1 << 2,
+    NO_VNDK = 1 << 3,
+    NO_KERNEL = 1 << 4,
+    NO_XMLFILES = 1 << 5,
+
+    EVERYTHING = 0,
+    HALS_ONLY = ~NO_HALS,
+};
+using SerializeFlags = uint32_t;
+
 template<typename Object>
 struct XmlConverter {
     XmlConverter() {}
     virtual ~XmlConverter() {}
     virtual const std::string &lastError() const = 0;
-    virtual std::string serialize(const Object &o) const = 0;
+    virtual std::string serialize(const Object& o, SerializeFlags flags = EVERYTHING) const = 0;
     virtual bool deserialize(Object *o, const std::string &xml) const = 0;
-    virtual std::string operator()(const Object &o) const = 0;
+    virtual std::string operator()(const Object& o, SerializeFlags flags = EVERYTHING) const = 0;
     virtual bool operator()(Object *o, const std::string &xml) const = 0;
 };
 
diff --git a/parse_xml.cpp b/parse_xml.cpp
index 350d10b..3df7e19 100644
--- a/parse_xml.cpp
+++ b/parse_xml.cpp
@@ -140,19 +140,23 @@
 
     // sub-types should implement these.
     virtual void mutateNode(const Object &o, NodeType *n, DocType *d) const = 0;
+    virtual void mutateNode(const Object& o, NodeType* n, DocType* d, SerializeFlags) const {
+        mutateNode(o, n, d);
+    }
     virtual bool buildObject(Object *o, NodeType *n) const = 0;
     virtual std::string elementName() const = 0;
 
     // convenience methods for user
     inline const std::string &lastError() const { return mLastError; }
-    inline NodeType *serialize(const Object &o, DocType *d) const {
+    inline NodeType* serialize(const Object& o, DocType* d,
+                               SerializeFlags flags = EVERYTHING) const {
         NodeType *root = createNode(this->elementName(), d);
-        this->mutateNode(o, root, d);
+        this->mutateNode(o, root, d, flags);
         return root;
     }
-    inline std::string serialize(const Object &o) const {
+    inline std::string serialize(const Object& o, SerializeFlags flags) const {
         DocType *doc = createDocument();
-        appendChild(doc, serialize(o, doc));
+        appendChild(doc, serialize(o, doc, flags));
         std::string s = printDocument(doc);
         deleteDocument(doc);
         return s;
@@ -176,8 +180,8 @@
     inline NodeType *operator()(const Object &o, DocType *d) const {
         return serialize(o, d);
     }
-    inline std::string operator()(const Object &o) const {
-        return serialize(o);
+    inline std::string operator()(const Object& o, SerializeFlags flags) const {
+        return serialize(o, flags);
     }
     inline bool operator()(Object *o, NodeType *node) const {
         return deserialize(o, node);
@@ -746,19 +750,32 @@
 struct HalManifestConverter : public XmlNodeConverter<HalManifest> {
     std::string elementName() const override { return "manifest"; }
     void mutateNode(const HalManifest &m, NodeType *root, DocType *d) const override {
+        mutateNode(m, root, d, SerializeFlag::EVERYTHING);
+    }
+    void mutateNode(const HalManifest& m, NodeType* root, DocType* d,
+                    SerializeFlags flags) const override {
         appendAttr(root, "version", m.getMetaVersion());
         appendAttr(root, "type", m.mType);
         if (m.mLevel != Level::UNSPECIFIED) {
             this->appendAttr(root, "target-level", m.mLevel);
         }
-        appendChildren(root, manifestHalConverter, m.getHals(), d);
+
+        if (!(flags & SerializeFlag::NO_HALS)) {
+            appendChildren(root, manifestHalConverter, m.getHals(), d);
+        }
         if (m.mType == SchemaType::DEVICE) {
-            appendChild(root, halManifestSepolicyConverter(m.device.mSepolicyVersion, d));
+            if (!(flags & SerializeFlag::NO_SEPOLICY)) {
+                appendChild(root, halManifestSepolicyConverter(m.device.mSepolicyVersion, d));
+            }
         } else if (m.mType == SchemaType::FRAMEWORK) {
-            appendChildren(root, vndkConverter, m.framework.mVndks, d);
+            if (!(flags & SerializeFlag::NO_VNDK)) {
+                appendChildren(root, vndkConverter, m.framework.mVndks, d);
+            }
         }
 
-        appendChildren(root, manifestXmlFileConverter, m.getXmlFiles(), d);
+        if (!(flags & SerializeFlag::NO_XMLFILES)) {
+            appendChildren(root, manifestXmlFileConverter, m.getXmlFiles(), d);
+        }
     }
     bool buildObject(HalManifest *object, NodeType *root) const override {
         std::vector<ManifestHal> hals;
@@ -859,21 +876,38 @@
 struct CompatibilityMatrixConverter : public XmlNodeConverter<CompatibilityMatrix> {
     std::string elementName() const override { return "compatibility-matrix"; }
     void mutateNode(const CompatibilityMatrix &m, NodeType *root, DocType *d) const override {
+        mutateNode(m, root, d, SerializeFlag::EVERYTHING);
+    }
+    void mutateNode(const CompatibilityMatrix& m, NodeType* root, DocType* d,
+                    SerializeFlags flags) const override {
         appendAttr(root, "version", m.getMinimumMetaVersion());
         appendAttr(root, "type", m.mType);
         if (m.mLevel != Level::UNSPECIFIED) {
             this->appendAttr(root, "level", m.mLevel);
         }
-        appendChildren(root, matrixHalConverter, iterateValues(m.mHals), d);
+
+        if (!(flags & SerializeFlag::NO_HALS)) {
+            appendChildren(root, matrixHalConverter, iterateValues(m.mHals), d);
+        }
         if (m.mType == SchemaType::FRAMEWORK) {
-            appendChildren(root, matrixKernelConverter, m.framework.mKernels, d);
-            appendChild(root, sepolicyConverter(m.framework.mSepolicy, d));
-            appendChild(root, avbConverter(m.framework.mAvbMetaVersion, d));
+            if (!(flags & SerializeFlag::NO_KERNEL)) {
+                appendChildren(root, matrixKernelConverter, m.framework.mKernels, d);
+            }
+            if (!(flags & SerializeFlag::NO_SEPOLICY)) {
+                appendChild(root, sepolicyConverter(m.framework.mSepolicy, d));
+            }
+            if (!(flags & SerializeFlag::NO_AVB)) {
+                appendChild(root, avbConverter(m.framework.mAvbMetaVersion, d));
+            }
         } else if (m.mType == SchemaType::DEVICE) {
-            appendChild(root, vndkConverter(m.device.mVndk, d));
+            if (!(flags & SerializeFlag::NO_VNDK)) {
+                appendChild(root, vndkConverter(m.device.mVndk, d));
+            }
         }
 
-        appendChildren(root, matrixXmlFileConverter, m.getXmlFiles(), d);
+        if (!(flags & SerializeFlag::NO_XMLFILES)) {
+            appendChildren(root, matrixXmlFileConverter, m.getXmlFiles(), d);
+        }
     }
     bool buildObject(CompatibilityMatrix *object, NodeType *root) const override {
         Version version;