Add <xmlfile> to manifest / comp mat.

manifest.xml is allowed to contain <xmlfile> that is similar to:
<xmlfile>
    <name>media_profile</name>
    <version>1.0</version>
    <path>/path/to/media_profile_v1_0.xml</path>
</xmlfile>

For compatibility-matrix.xml
<xmlfile format="dtd" optional="false">
    <name>media_profile</name>
    <version>1.0-1</version>
    <path>/path/to/media_profile_v1_1.dtd</path>
</xmlfile>

The <path> tag is optional. See test/main.cpp for examples.

Test: libvintf_test
Test: vintf_object_test

Bug: 38359330

Change-Id: I0a921f32d023e1ba9c54ea6e898bb385fc7abd4f
diff --git a/parse_xml.cpp b/parse_xml.cpp
index 557da76..f12df00 100644
--- a/parse_xml.cpp
+++ b/parse_xml.cpp
@@ -265,6 +265,13 @@
         return true;
     }
 
+    inline bool parseOptionalTextElement(NodeType* root, const std::string& elementName,
+                                         std::string&& defaultValue, std::string* s) const {
+        NodeType* child = getChild(root, elementName);
+        *s = child == nullptr ? std::move(defaultValue) : getText(child);
+        return true;
+    }
+
     inline bool parseTextElements(NodeType *root, const std::string &elementName,
             std::vector<std::string> *v) const {
         auto nodes = getChildren(root, elementName);
@@ -610,17 +617,40 @@
 };
 const HalManifestSepolicyConverter halManifestSepolicyConverter{};
 
+struct ManifestXmlFileConverter : public XmlNodeConverter<ManifestXmlFile> {
+    std::string elementName() const override { return "xmlfile"; }
+    void mutateNode(const ManifestXmlFile& f, NodeType* root, DocType* d) const override {
+        appendTextElement(root, "name", f.name(), d);
+        appendChild(root, versionConverter(f.version(), d));
+        if (!f.overriddenPath().empty()) {
+            appendTextElement(root, "path", f.overriddenPath(), d);
+        }
+    }
+    bool buildObject(ManifestXmlFile* object, NodeType* root) const override {
+        if (!parseTextElement(root, "name", &object->mName) ||
+            !parseChild(root, versionConverter, &object->mVersion) ||
+            !parseOptionalTextElement(root, "path", {}, &object->mOverriddenPath)) {
+            return false;
+        }
+        return true;
+    }
+};
+const ManifestXmlFileConverter manifestXmlFileConverter{};
+
 struct HalManifestConverter : public XmlNodeConverter<HalManifest> {
     std::string elementName() const override { return "manifest"; }
     void mutateNode(const HalManifest &m, NodeType *root, DocType *d) const override {
         appendAttr(root, "version", HalManifest::kVersion);
         appendAttr(root, "type", m.mType);
+
         appendChildren(root, manifestHalConverter, m.getHals(), d);
         if (m.mType == SchemaType::DEVICE) {
             appendChild(root, halManifestSepolicyConverter(m.device.mSepolicyVersion, d));
         } else if (m.mType == SchemaType::FRAMEWORK) {
             appendChildren(root, vndkConverter, m.framework.mVndks, d);
         }
+
+        appendChildren(root, manifestXmlFileConverter, m.getXmlFiles(), d);
     }
     bool buildObject(HalManifest *object, NodeType *root) const override {
         Version version;
@@ -661,6 +691,20 @@
                 return false;
             }
         }
+
+        std::vector<ManifestXmlFile> xmlFiles;
+        if (!parseChildren(root, manifestXmlFileConverter, &xmlFiles)) {
+            return false;
+        }
+        for (auto&& xmlFile : xmlFiles) {
+            std::string description{xmlFile.name()};
+            if (!object->addXmlFile(std::move(xmlFile))) {
+                this->mLastError = "Duplicated manifest.xmlfile entry " + description +
+                                   "; entries cannot have duplicated name and version";
+                return false;
+            }
+        }
+
         return true;
     }
 };
@@ -679,6 +723,30 @@
 };
 const AvbConverter avbConverter{};
 
+struct MatrixXmlFileConverter : public XmlNodeConverter<MatrixXmlFile> {
+    std::string elementName() const override { return "xmlfile"; }
+    void mutateNode(const MatrixXmlFile& f, NodeType* root, DocType* d) const override {
+        appendTextElement(root, "name", f.name(), d);
+        appendAttr(root, "format", f.format());
+        appendAttr(root, "optional", f.optional());
+        appendChild(root, versionRangeConverter(f.versionRange(), d));
+        if (!f.overriddenPath().empty()) {
+            appendTextElement(root, "path", f.overriddenPath(), d);
+        }
+    }
+    bool buildObject(MatrixXmlFile* object, NodeType* root) const override {
+        if (!parseTextElement(root, "name", &object->mName) ||
+            !parseAttr(root, "format", &object->mFormat) ||
+            !parseOptionalAttr(root, "optional", false, &object->mOptional) ||
+            !parseChild(root, versionRangeConverter, &object->mVersionRange) ||
+            !parseOptionalTextElement(root, "path", {}, &object->mOverriddenPath)) {
+            return false;
+        }
+        return true;
+    }
+};
+const MatrixXmlFileConverter matrixXmlFileConverter{};
+
 struct CompatibilityMatrixConverter : public XmlNodeConverter<CompatibilityMatrix> {
     std::string elementName() const override { return "compatibility-matrix"; }
     void mutateNode(const CompatibilityMatrix &m, NodeType *root, DocType *d) const override {
@@ -692,6 +760,8 @@
         } else if (m.mType == SchemaType::DEVICE) {
             appendChild(root, vndkConverter(m.device.mVndk, d));
         }
+
+        appendChildren(root, matrixXmlFileConverter, m.getXmlFiles(), d);
     }
     bool buildObject(CompatibilityMatrix *object, NodeType *root) const override {
         Version version;
@@ -728,6 +798,24 @@
                 return false;
             }
         }
+
+        std::vector<MatrixXmlFile> xmlFiles;
+        if (!parseChildren(root, matrixXmlFileConverter, &xmlFiles)) {
+            return false;
+        }
+        for (auto&& xmlFile : xmlFiles) {
+            if (!xmlFile.optional()) {
+                this->mLastError = "compatibility-matrix.xmlfile entry " + xmlFile.name() +
+                                   " has to be optional for compatibility matrix version 1.0";
+                return false;
+            }
+            std::string description{xmlFile.name()};
+            if (!object->addXmlFile(std::move(xmlFile))) {
+                this->mLastError = "Duplicated compatibility-matrix.xmlfile entry " + description;
+                return false;
+            }
+        }
+
         return true;
     }
 };