Match /proc/config.gz with fwk comp mat <kernel> fragments

Multiple <kernel> elements with the same version can
exist. The <condition> element decides whether the <kernel>
element is actually used during matching.

Test: libvintf_test
Bug: 64124223

Change-Id: I0dd28b8b1bdfe06a4da61aaa81df91f4df5fcf26
Merged-In: I0dd28b8b1bdfe06a4da61aaa81df91f4df5fcf26
diff --git a/RuntimeInfo.cpp b/RuntimeInfo.cpp
index b29a146..0bcb363 100644
--- a/RuntimeInfo.cpp
+++ b/RuntimeInfo.cpp
@@ -69,6 +69,38 @@
     return mBootAvbVersion;
 }
 
+bool RuntimeInfo::matchKernelConfigs(const std::vector<KernelConfig>& matrixConfigs,
+                                     std::string* error) const {
+    for (const KernelConfig& matrixConfig : matrixConfigs) {
+        const std::string& key = matrixConfig.first;
+        auto it = this->mKernelConfigs.find(key);
+        if (it == this->mKernelConfigs.end()) {
+            // special case: <value type="tristate">n</value> matches if the config doesn't exist.
+            if (matrixConfig.second == KernelConfigTypedValue::gMissingConfig) {
+                continue;
+            }
+            if (error != nullptr) {
+                *error = "Missing config " + key;
+            }
+            return false;
+        }
+        const std::string& kernelValue = it->second;
+        if (!matrixConfig.second.matchValue(kernelValue)) {
+            if (error != nullptr) {
+                *error = "For config " + key + ", value = " + kernelValue + " but required " +
+                         to_string(matrixConfig.second);
+            }
+            return false;
+        }
+    }
+    return true;
+}
+
+bool RuntimeInfo::matchKernelVersion(const KernelVersion& minLts) const {
+    return minLts.version == mKernelVersion.version && minLts.majorRev == mKernelVersion.majorRev &&
+           minLts.minorRev <= mKernelVersion.minorRev;
+}
+
 bool RuntimeInfo::checkCompatibility(const CompatibilityMatrix &mat,
             std::string *error) const {
     if (mat.mType != SchemaType::FRAMEWORK) {
@@ -86,36 +118,46 @@
         return false;
     }
 
-    // mat.mSepolicy.sepolicyVersion() is checked against static HalManifest.device.mSepolicyVersion
+    // mat.mSepolicy.sepolicyVersion() is checked against static
+    // HalManifest.device.mSepolicyVersion in HalManifest::checkCompatibility.
 
-    const MatrixKernel *matrixKernel = mat.findKernel(this->mKernelVersion);
-    if (matrixKernel == nullptr) {
+    bool foundMatchedKernelVersion = false;
+    bool foundMatchedConditions = false;
+    for (const MatrixKernel& matrixKernel : mat.framework.mKernels) {
+        if (!matchKernelVersion(matrixKernel.minLts())) {
+            continue;
+        }
+        foundMatchedKernelVersion = true;
+        // ignore this fragment if not all conditions are met.
+        if (!matchKernelConfigs(matrixKernel.conditions(), error)) {
+            continue;
+        }
+        foundMatchedConditions = true;
+        if (!matchKernelConfigs(matrixKernel.configs(), error)) {
+            return false;
+        }
+    }
+    if (!foundMatchedKernelVersion) {
         if (error != nullptr) {
-            *error = "Cannot find suitable kernel entry for " + to_string(mKernelVersion);
+            std::stringstream ss;
+            ss << "Framework is incompatible with kernel version " << mKernelVersion
+               << ", compatible kernel versions are";
+            for (const MatrixKernel& matrixKernel : mat.framework.mKernels)
+                ss << " " << matrixKernel.minLts();
+            *error = ss.str();
         }
         return false;
     }
-    for (const KernelConfig &matrixConfig : matrixKernel->configs()) {
-        const std::string &key = matrixConfig.first;
-        auto it = this->mKernelConfigs.find(key);
-        if (it == this->mKernelConfigs.end()) {
-            // special case: <value type="tristate">n</value> matches if the config doesn't exist.
-            if (matrixConfig.second == KernelConfigTypedValue::gMissingConfig) {
-                continue;
-            }
-            if (error != nullptr) {
-                *error = "Missing config " + key;
-            }
-            return false;
+    if (!foundMatchedConditions) {
+        // This should not happen because first <conditions> for each <kernel> must be
+        // empty. Reject here for inconsistency.
+        if (error != nullptr) {
+            error->insert(0, "Framework match kernel version with unmet conditions:");
         }
-        const std::string &kernelValue = it->second;
-        if (!matrixConfig.second.matchValue(kernelValue)) {
-            if (error != nullptr) {
-                *error = "For config " + key + ", value = " + kernelValue
-                        + " but required " + to_string(matrixConfig.second);
-            }
-            return false;
-        }
+        return false;
+    }
+    if (error != nullptr) {
+        error->clear();
     }
 
     const Version &matAvb = mat.framework.mAvbMetaVersion;