Add getXmlFilePath for manifest / matrix.

Return <path> or if it doesn't exist,
/{system,vendor}/etc/<name>_V<major>_<minor>.<format>

Test: libvintf_test
Test: vintf_object_test

Bug: 38359330

Change-Id: I80eff960a5dea91521e99e628e1af7cf52dad195
diff --git a/CompatibilityMatrix.cpp b/CompatibilityMatrix.cpp
index 907ac8c..509ca1d 100644
--- a/CompatibilityMatrix.cpp
+++ b/CompatibilityMatrix.cpp
@@ -16,6 +16,7 @@
 
 #include "CompatibilityMatrix.h"
 
+#include "parse_string.h"
 #include "utils.h"
 
 namespace android {
@@ -57,6 +58,25 @@
     return details::fetchAllInformation(path, gCompatibilityMatrixConverter, this);
 }
 
+std::string CompatibilityMatrix::getXmlSchemaPath(const std::string& xmlFileName,
+                                                  const Version& version) const {
+    using std::literals::string_literals::operator""s;
+    auto range = getXmlFiles(xmlFileName);
+    for (auto it = range.first; it != range.second; ++it) {
+        const MatrixXmlFile& matrixXmlFile = it->second;
+        if (matrixXmlFile.versionRange().contains(version)) {
+            if (!matrixXmlFile.overriddenPath().empty()) {
+                return matrixXmlFile.overriddenPath();
+            }
+            return "/"s + (type() == SchemaType::DEVICE ? "vendor" : "system") + "/etc/" +
+                   xmlFileName + "_V" + std::to_string(matrixXmlFile.versionRange().majorVer) +
+                   "_" + std::to_string(matrixXmlFile.versionRange().maxMinor) + "." +
+                   to_string(matrixXmlFile.format());
+        }
+    }
+    return "";
+}
+
 bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) {
     return lft.mType == rgt.mType && lft.mHals == rgt.mHals && lft.mXmlFiles == rgt.mXmlFiles &&
            (lft.mType != SchemaType::DEVICE || (lft.device.mVndk == rgt.device.mVndk)) &&
diff --git a/HalManifest.cpp b/HalManifest.cpp
index 5ebe083..f0fcf3f 100644
--- a/HalManifest.cpp
+++ b/HalManifest.cpp
@@ -374,6 +374,24 @@
     return framework.mVndks;
 }
 
+std::string HalManifest::getXmlFilePath(const std::string& xmlFileName,
+                                        const Version& version) const {
+    using std::literals::string_literals::operator""s;
+    auto range = getXmlFiles(xmlFileName);
+    for (auto it = range.first; it != range.second; ++it) {
+        const ManifestXmlFile& manifestXmlFile = it->second;
+        if (manifestXmlFile.version() == version) {
+            if (!manifestXmlFile.overriddenPath().empty()) {
+                return manifestXmlFile.overriddenPath();
+            }
+            return "/"s + (type() == SchemaType::DEVICE ? "vendor" : "system") + "/etc/" +
+                   xmlFileName + "_V" + std::to_string(version.majorVer) + "_" +
+                   std::to_string(version.minorVer) + ".xml";
+        }
+    }
+    return "";
+}
+
 bool operator==(const HalManifest &lft, const HalManifest &rgt) {
     return lft.mType == rgt.mType && lft.mHals == rgt.mHals && lft.mXmlFiles == rgt.mXmlFiles &&
            (lft.mType != SchemaType::DEVICE ||
diff --git a/include/vintf/CompatibilityMatrix.h b/include/vintf/CompatibilityMatrix.h
index 66e5f8d..94b8004 100644
--- a/include/vintf/CompatibilityMatrix.h
+++ b/include/vintf/CompatibilityMatrix.h
@@ -43,7 +43,18 @@
 
     constexpr static Version kVersion{1, 0};
 
-private:
+    // If the corresponding <xmlfile> with the given version exists, for the first match,
+    // - Return the overridden <path> if it is present,
+    // - otherwise the default value: /{system,vendor}/etc/<name>_V<major>_<minor-max>.<format>
+    // Otherwise if the <xmlfile> entry does not exist, "" is returned.
+    // For example, if the matrix says ["audio@1.0-5" -> "foo.xml", "audio@1.3-7" -> bar.xml]
+    // getXmlSchemaPath("audio", 1.0) -> foo.xml
+    // getXmlSchemaPath("audio", 1.5) -> foo.xml
+    // getXmlSchemaPath("audio", 1.7) -> bar.xml
+    // (Normally, version ranges do not overlap, and the only match is returned.)
+    std::string getXmlSchemaPath(const std::string& xmlFileName, const Version& version) const;
+
+   private:
     bool add(MatrixHal &&hal);
     bool add(MatrixKernel &&kernel);
 
diff --git a/include/vintf/HalManifest.h b/include/vintf/HalManifest.h
index 2f78301..31c4801 100644
--- a/include/vintf/HalManifest.h
+++ b/include/vintf/HalManifest.h
@@ -121,6 +121,12 @@
     // Abort if type != framework.
     const std::vector<Vndk> &vndks() const;
 
+    // If the corresponding <xmlfile> with the given version exists,
+    // - Return the overridden <path> if it is present,
+    // - otherwise the default value: /{system,vendor}/etc/<name>_V<major>_<minor>.xml
+    // Otherwise if the <xmlfile> entry does not exist, "" is returned.
+    std::string getXmlFilePath(const std::string& xmlFileName, const Version& version) const;
+
    protected:
     // Check before add()
     bool shouldAdd(const ManifestHal& toAdd) const override;
diff --git a/test/main.cpp b/test/main.cpp
index 090fd3d..25694b9 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -1198,6 +1198,116 @@
         gCompatibilityMatrixConverter.lastError());
 }
 
+TEST_F(LibVintfTest, ManifestXmlFilePathDevice) {
+    std::string manifestXml =
+        "<manifest version=\"1.0\" type=\"device\">"
+        "    <xmlfile>"
+        "        <name>media_profile</name>"
+        "        <version>1.0</version>"
+        "    </xmlfile>"
+        "</manifest>";
+    HalManifest manifest;
+    EXPECT_TRUE(gHalManifestConverter(&manifest, manifestXml));
+    EXPECT_EQ(manifest.getXmlFilePath("media_profile", {1, 0}),
+              "/vendor/etc/media_profile_V1_0.xml");
+}
+
+TEST_F(LibVintfTest, ManifestXmlFilePathFramework) {
+    std::string manifestXml =
+        "<manifest version=\"1.0\" type=\"framework\">"
+        "    <xmlfile>"
+        "        <name>media_profile</name>"
+        "        <version>1.0</version>"
+        "    </xmlfile>"
+        "</manifest>";
+    HalManifest manifest;
+    EXPECT_TRUE(gHalManifestConverter(&manifest, manifestXml));
+    EXPECT_EQ(manifest.getXmlFilePath("media_profile", {1, 0}),
+              "/system/etc/media_profile_V1_0.xml");
+}
+
+TEST_F(LibVintfTest, ManifestXmlFilePathOverride) {
+    std::string manifestXml =
+        "<manifest version=\"1.0\" type=\"device\">"
+        "    <xmlfile>"
+        "        <name>media_profile</name>"
+        "        <version>1.0</version>"
+        "        <path>/vendor/etc/foo.xml</path>"
+        "    </xmlfile>"
+        "</manifest>";
+    HalManifest manifest;
+    EXPECT_TRUE(gHalManifestConverter(&manifest, manifestXml));
+    EXPECT_EQ(manifest.getXmlFilePath("media_profile", {1, 0}), "/vendor/etc/foo.xml");
+}
+
+TEST_F(LibVintfTest, ManifestXmlFilePathMissing) {
+    std::string manifestXml =
+        "<manifest version=\"1.0\" type=\"device\">"
+        "    <xmlfile>"
+        "        <name>media_profile</name>"
+        "        <version>1.1</version>"
+        "    </xmlfile>"
+        "</manifest>";
+    HalManifest manifest;
+    EXPECT_TRUE(gHalManifestConverter(&manifest, manifestXml));
+    EXPECT_EQ(manifest.getXmlFilePath("media_profile", {1, 0}), "");
+}
+
+TEST_F(LibVintfTest, MatrixXmlFilePathFramework) {
+    std::string matrixXml =
+        "<compatibility-matrix version=\"1.0\" type=\"framework\">"
+        "    <xmlfile format=\"dtd\" optional=\"true\">"
+        "        <name>media_profile</name>"
+        "        <version>2.0-1</version>"
+        "    </xmlfile>"
+        "</compatibility-matrix>";
+    CompatibilityMatrix matrix;
+    EXPECT_TRUE(gCompatibilityMatrixConverter(&matrix, matrixXml));
+    EXPECT_EQ(matrix.getXmlSchemaPath("media_profile", {2, 1}),
+              "/system/etc/media_profile_V2_1.dtd");
+}
+
+TEST_F(LibVintfTest, MatrixXmlFilePathDevice) {
+    std::string matrixXml =
+        "<compatibility-matrix version=\"1.0\" type=\"device\">"
+        "    <xmlfile format=\"xsd\" optional=\"true\">"
+        "        <name>media_profile</name>"
+        "        <version>2.0-1</version>"
+        "    </xmlfile>"
+        "</compatibility-matrix>";
+    CompatibilityMatrix matrix;
+    EXPECT_TRUE(gCompatibilityMatrixConverter(&matrix, matrixXml));
+    EXPECT_EQ(matrix.getXmlSchemaPath("media_profile", {2, 0}),
+              "/vendor/etc/media_profile_V2_1.xsd");
+}
+
+TEST_F(LibVintfTest, MatrixXmlFilePathOverride) {
+    std::string matrixXml =
+        "<compatibility-matrix version=\"1.0\" type=\"framework\">"
+        "    <xmlfile format=\"xsd\" optional=\"true\">"
+        "        <name>media_profile</name>"
+        "        <version>2.0-1</version>"
+        "        <path>/system/etc/foo.xsd</path>"
+        "    </xmlfile>"
+        "</compatibility-matrix>";
+    CompatibilityMatrix matrix;
+    EXPECT_TRUE(gCompatibilityMatrixConverter(&matrix, matrixXml));
+    EXPECT_EQ(matrix.getXmlSchemaPath("media_profile", {2, 0}), "/system/etc/foo.xsd");
+}
+
+TEST_F(LibVintfTest, MatrixXmlFilePathMissing) {
+    std::string matrixXml =
+        "<compatibility-matrix version=\"1.0\" type=\"framework\">"
+        "    <xmlfile format=\"dtd\" optional=\"true\">"
+        "        <name>media_profile</name>"
+        "        <version>2.1</version>"
+        "    </xmlfile>"
+        "</compatibility-matrix>";
+    CompatibilityMatrix matrix;
+    EXPECT_TRUE(gCompatibilityMatrixConverter(&matrix, matrixXml));
+    EXPECT_EQ(matrix.getXmlSchemaPath("media_profile", {2, 0}), "");
+}
+
 } // namespace vintf
 } // namespace android