Add product matrix FCM levels.

Read all files in /product/etc/vintf and treat files the same way
we treat system matrix at each FCM level in /system/etc/vintf.

Bug: 140360109
Test: vintf_object_test

Change-Id: I0a74b7aa3001c4491f264f36bb2093cd97ac04bf
diff --git a/VintfObject.cpp b/VintfObject.cpp
index b706599..5c9f8d9 100644
--- a/VintfObject.cpp
+++ b/VintfObject.cpp
@@ -23,6 +23,7 @@
 #include <mutex>
 
 #include <android-base/logging.h>
+#include <android-base/strings.h>
 
 #include "CompatibilityMatrix.h"
 #include "parse_string.h"
@@ -401,43 +402,48 @@
 
 status_t VintfObject::getAllFrameworkMatrixLevels(std::vector<Named<CompatibilityMatrix>>* results,
                                                   std::string* error) {
-    std::vector<std::string> fileNames;
-
-    status_t listStatus = getFileSystem()->listFiles(kSystemVintfDir, &fileNames, error);
-    if (listStatus != OK) {
-        return listStatus;
-    }
-    for (const std::string& fileName : fileNames) {
-        std::string path = kSystemVintfDir + fileName;
-        Named<CompatibilityMatrix> namedMatrix;
-        std::string matrixError;
-        status_t matrixStatus = getOneMatrix(path, &namedMatrix, &matrixError);
-        if (matrixStatus != OK) {
-            // System manifests and matrices share the same dir. Client may not have enough
-            // permissions to read system manifests, or may not be able to parse it.
-            auto logLevel = matrixStatus == BAD_VALUE ? base::DEBUG : base::ERROR;
-            LOG(logLevel) << "Framework Matrix: Ignore file " << path << ": " << matrixError;
+    std::vector<std::string> dirs = {
+        kSystemVintfDir,
+        kProductVintfDir,
+    };
+    for (const auto& dir : dirs) {
+        std::vector<std::string> fileNames;
+        status_t listStatus = getFileSystem()->listFiles(dir, &fileNames, error);
+        if (listStatus == NAME_NOT_FOUND) {
             continue;
         }
-        results->emplace_back(std::move(namedMatrix));
-    }
+        if (listStatus != OK) {
+            return listStatus;
+        }
+        for (const std::string& fileName : fileNames) {
+            std::string path = dir + fileName;
+            Named<CompatibilityMatrix> namedMatrix;
+            std::string matrixError;
+            status_t matrixStatus = getOneMatrix(path, &namedMatrix, &matrixError);
+            if (matrixStatus != OK) {
+                // Manifests and matrices share the same dir. Client may not have enough
+                // permissions to read system manifests, or may not be able to parse it.
+                auto logLevel = matrixStatus == BAD_VALUE ? base::DEBUG : base::ERROR;
+                LOG(logLevel) << "Framework Matrix: Ignore file " << path << ": " << matrixError;
+                continue;
+            }
+            results->emplace_back(std::move(namedMatrix));
+        }
 
-    Named<CompatibilityMatrix> productMatrix;
-    std::string productError;
-    status_t productStatus = getOneMatrix(kProductMatrix, &productMatrix, &productError);
-    if (productStatus == OK) {
-        results->emplace_back(std::move(productMatrix));
-    } else if (productStatus == NAME_NOT_FOUND) {
-        LOG(DEBUG) << "Framework Matrix: missing " << kProductMatrix;
-    } else {
-        if (error) *error = std::move(productError);
-        return productStatus;
+        if (dir == kSystemVintfDir && results->empty()) {
+            if (error) {
+                *error = "No framework matrices under " + dir + " can be fetched or parsed.\n";
+            }
+            return NAME_NOT_FOUND;
+        }
     }
 
     if (results->empty()) {
         if (error) {
             *error =
-                "No framework matrices under " + kSystemVintfDir + " can be fetched or parsed.\n";
+                "No framework matrices can be fetched or parsed. "
+                "The following directories are searched:\n  " +
+                android::base::Join(dirs, "\n  ");
         }
         return NAME_NOT_FOUND;
     }
diff --git a/test/vintf_object_tests.cpp b/test/vintf_object_tests.cpp
index bcf753c..31e54a1 100644
--- a/test/vintf_object_tests.cpp
+++ b/test/vintf_object_tests.cpp
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <stdio.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <hidl-util/FQName.h>
 
@@ -32,6 +31,7 @@
 #include "utils-fake.h"
 
 using namespace ::testing;
+using namespace std::literals;
 
 using android::FqInstance;
 
@@ -618,6 +618,11 @@
             };
             return ::android::OK;
         }));
+    EXPECT_CALL(fetcher(), listFiles(StrEq(kProductVintfDir), _, _))
+        .WillRepeatedly(Invoke([](const auto&, auto* out, auto*) {
+            *out = {android::base::Basename(kProductMatrix)};
+            return ::android::OK;
+        }));
     expectFetch(kSystemVintfDir + "compatibility_matrix.1.xml",
                 "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"1\"/>");
     expectFetch(kSystemVintfDir + "compatibility_matrix.empty.xml",
@@ -983,7 +988,10 @@
         return "compatibility_matrix." + std::to_string(static_cast<Level>(i)) + ".xml";
     }
     void SetUpMockSystemMatrices(const std::vector<std::string>& xmls) {
-        EXPECT_CALL(fetcher(), listFiles(StrEq(kSystemVintfDir), _, _))
+        SetUpMockMatrices(kSystemVintfDir, xmls);
+    }
+    void SetUpMockMatrices(const std::string& dir, const std::vector<std::string>& xmls) {
+        EXPECT_CALL(fetcher(), listFiles(StrEq(dir), _, _))
             .WillRepeatedly(Invoke([=](const auto&, auto* out, auto*) {
                 size_t i = 1;
                 for (const auto& content : xmls) {
@@ -995,12 +1003,9 @@
             }));
         size_t i = 1;
         for (const auto& content : xmls) {
-            expectFetchRepeatedly(kSystemVintfDir + getFileName(i), content);
+            expectFetchRepeatedly(dir + getFileName(i), content);
             ++i;
         }
-        expectFileNotExist(kProductMatrix);
-        expectNeverFetch(kSystemLegacyMatrix);
-        expectFileNotExist(StartsWith("/odm/"));
     }
     void expectTargetFcmVersion(size_t level) {
         expectFetch(kVendorManifest, "<manifest " + kMetaVersionStr + " type=\"device\" target-level=\"" +
@@ -1578,6 +1583,91 @@
 INSTANTIATE_TEST_SUITE_P(Vintf, FrameworkManifestTest,
                          ::testing::Combine(Bool(), Bool(), Bool(), Bool()));
 
+
+//
+// Set of OEM FCM matrices at different FCM version.
+//
+
+std::vector<std::string> GetOemFcmMatrixLevels(const std::string& name) {
+    return {
+        // 1.xml
+        "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"1\">\n"
+        "    <hal format=\"hidl\" optional=\"true\">\n"
+        "        <name>vendor.foo." + name + "</name>\n"
+        "        <version>1.0</version>\n"
+        "        <interface>\n"
+        "            <name>IExtra</name>\n"
+        "            <instance>default</instance>\n"
+        "        </interface>\n"
+        "    </hal>\n"
+        "</compatibility-matrix>\n",
+        // 2.xml
+        "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"2\">\n"
+        "    <hal format=\"hidl\" optional=\"true\">\n"
+        "        <name>vendor.foo." + name + "</name>\n"
+        "        <version>2.0</version>\n"
+        "        <interface>\n"
+        "            <name>IExtra</name>\n"
+        "            <instance>default</instance>\n"
+        "        </interface>\n"
+        "    </hal>\n"
+        "</compatibility-matrix>\n",
+    };
+}
+
+class OemFcmLevelTest : public MultiMatrixTest,
+                        public WithParamInterface<std::tuple<size_t, bool>> {
+   protected:
+    virtual void SetUp() override {
+        MultiMatrixTest::SetUp();
+        SetUpMockSystemMatrices({systemMatrixLevel1, systemMatrixLevel2});
+    }
+    using Instances = std::set<std::string>;
+    Instances GetInstances(const CompatibilityMatrix* fcm) {
+        Instances instances;
+        fcm->forEachHidlInstance([&instances](const auto& matrixInstance) {
+            instances.insert(matrixInstance.description(matrixInstance.versionRange().minVer()));
+            return true; // continue
+        });
+        return instances;
+    }
+};
+
+TEST_P(OemFcmLevelTest, Test) {
+    auto&& [level, hasProduct] = GetParam();
+
+    expectTargetFcmVersion(level);
+    if (hasProduct) {
+        SetUpMockMatrices(kProductVintfDir, GetOemFcmMatrixLevels("product"));
+    }
+
+    auto fcm = vintfObject->getFrameworkCompatibilityMatrix();
+    ASSERT_NE(nullptr, fcm);
+    auto instances = GetInstances(fcm.get());
+
+    auto containsOrNot = [](bool contains, const std::string& e) {
+        return contains ? SafeMatcherCast<Instances>(Contains(e))
+                        : SafeMatcherCast<Instances>(Not(Contains(e)));
+    };
+
+    EXPECT_THAT(instances, containsOrNot(level == 1,
+                                         "android.hardware.major@1.0::IMajor/default"));
+    EXPECT_THAT(instances, containsOrNot(level == 1 && hasProduct,
+                                         "vendor.foo.product@1.0::IExtra/default"));
+    EXPECT_THAT(instances, Contains("android.hardware.major@2.0::IMajor/default"));
+    EXPECT_THAT(instances, containsOrNot(hasProduct,
+                                         "vendor.foo.product@2.0::IExtra/default"));
+}
+
+static std::string OemFcmLevelTestParamToString(
+        const TestParamInfo<OemFcmLevelTest::ParamType>& info) {
+    auto&& [level, hasProduct] = info.param;
+    auto name = "Level" + std::to_string(level);
+    name += "With"s + (hasProduct ? "" : "out") + "Product";
+    return name;
+}
+INSTANTIATE_TEST_SUITE_P(OemFcmLevel, OemFcmLevelTest, Combine(Values(1, 2), Bool()),
+    OemFcmLevelTestParamToString);
 // clang-format on
 
 }  // namespace testing