assemble_vintf: build and fuse compatibility matrix at build time.

The fused matrix:
(1) has all requirements in compatibilility_matrix.V.xml, where V
is the "level" attribute in device manifest (or is inferred if missing)
(2) has all other HALs as optional HALs in compatibility_matrix.U.xml
files, where U > V, including current.xml.

Bug: 69636193

Test: m (system_)compatibility_matrix/manifest.xml
Test: libvintf_test

Change-Id: I01d2f1ea779e270a9b3ca8de63a064747f36eb2d
Merged-In: I01d2f1ea779e270a9b3ca8de63a064747f36eb2d
diff --git a/CompatibilityMatrix.cpp b/CompatibilityMatrix.cpp
index f788569..521c44b 100644
--- a/CompatibilityMatrix.cpp
+++ b/CompatibilityMatrix.cpp
@@ -16,6 +16,8 @@
 
 #include "CompatibilityMatrix.h"
 
+#include <utility>
+
 #include "parse_string.h"
 #include "utils.h"
 
@@ -71,6 +73,63 @@
     return "";
 }
 
+static VersionRange* findRangeWithMajorVersion(std::vector<VersionRange>& versionRanges,
+                                               size_t majorVer) {
+    for (VersionRange& vr : versionRanges) {
+        if (vr.majorVer == majorVer) {
+            return &vr;
+        }
+    }
+    return nullptr;
+}
+
+std::pair<MatrixHal*, VersionRange*> CompatibilityMatrix::getHalWithMajorVersion(
+    const std::string& name, size_t majorVer) {
+    for (MatrixHal* hal : getHals(name)) {
+        VersionRange* vr = findRangeWithMajorVersion(hal->versionRanges, majorVer);
+        if (vr != nullptr) {
+            return {hal, vr};
+        }
+    }
+    return {nullptr, nullptr};
+}
+
+bool CompatibilityMatrix::addAllHalsAsOptional(CompatibilityMatrix* other, std::string* error) {
+    if (other == nullptr || other->level() <= level()) {
+        return true;
+    }
+
+    for (auto& pair : other->mHals) {
+        const std::string& name = pair.first;
+        MatrixHal& halToAdd = pair.second;
+        for (const VersionRange& vr : halToAdd.versionRanges) {
+            MatrixHal* existingHal;
+            VersionRange* existingVr;
+            std::tie(existingHal, existingVr) = getHalWithMajorVersion(name, vr.majorVer);
+
+            if (existingHal == nullptr) {
+                halToAdd.optional = true;
+                add(std::move(halToAdd));
+                continue;
+            }
+
+            if (!existingHal->optional && !existingHal->containsInstances(halToAdd)) {
+                if (error != nullptr) {
+                    *error = "HAL " + name + "@" + to_string(vr.minVer()) + " is a required " +
+                             "HAL, but fully qualified instance names don't match (at FCM "
+                             "Version " +
+                             std::to_string(level()) + " and " + std::to_string(other->level()) +
+                             ")";
+                }
+                return false;
+            }
+
+            existingVr->maxMinor = std::max(existingVr->maxMinor, vr.maxMinor);
+        }
+    }
+    return true;
+}
+
 bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) {
     return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals &&
            lft.mXmlFiles == rgt.mXmlFiles &&