Add HalManifest::shouldCheckKernelCompatibility

Now that <kernel> may exist in a HAL manifest without version and
configs, HalManifest::kernel().has_value() does not indicate whether
kernel information exists.

Add a function, shouldCheckKernelCompatibility(), that also checks
the existance of kernel version. kernel()->checkCompatibility may
be called if and only if this function returns true.

Test: cuttlefish vintf vts test
Test: libvintf_test
Test: vintf_object_test

Change-Id: I147703da6823275ceed9a134a75aefaa0ceae099
diff --git a/HalManifest.cpp b/HalManifest.cpp
index 6d2eafc..bd0379a 100644
--- a/HalManifest.cpp
+++ b/HalManifest.cpp
@@ -347,7 +347,7 @@
             return false;
         }
 
-        if (flags.isKernelEnabled() && !!kernel() &&
+        if (flags.isKernelEnabled() && shouldCheckKernelCompatibility() &&
             kernel()
                 ->getMatchedKernelRequirements(mat.framework.mKernels, kernel()->level(), error)
                 .empty()) {
@@ -358,6 +358,10 @@
     return true;
 }
 
+bool HalManifest::shouldCheckKernelCompatibility() const {
+    return kernel().has_value() && kernel()->version() != KernelVersion{};
+}
+
 CompatibilityMatrix HalManifest::generateCompatibleMatrix() const {
     CompatibilityMatrix matrix;
 
diff --git a/VintfObject.cpp b/VintfObject.cpp
index c0ce79b..15ea4d2 100644
--- a/VintfObject.cpp
+++ b/VintfObject.cpp
@@ -602,7 +602,7 @@
     }
 
     CheckFlags::Type runtimeInfoCheckFlags = flags;
-    if (!!getDeviceHalManifest()->kernel()) {
+    if (getDeviceHalManifest()->shouldCheckKernelCompatibility()) {
         // Use kernel from incoming OTA package, but not on the device.
         runtimeInfoCheckFlags = runtimeInfoCheckFlags.disableKernel();
     }
diff --git a/include/vintf/HalManifest.h b/include/vintf/HalManifest.h
index ee900e6..e7b2a0e 100644
--- a/include/vintf/HalManifest.h
+++ b/include/vintf/HalManifest.h
@@ -198,6 +198,10 @@
     // Merge information of other to this.
     bool mergeKernel(std::optional<KernelInfo>* other, std::string* error = nullptr);
 
+    // Whether the manifest contains information about the kernel for compatibility checks.
+    // True if kernel()->checkCompatibility can be called.
+    bool shouldCheckKernelCompatibility() const;
+
     SchemaType mType;
     Level mLevel = Level::UNSPECIFIED;
 
diff --git a/test/vintf_object_tests.cpp b/test/vintf_object_tests.cpp
index 30bfc70..f92db0e 100644
--- a/test/vintf_object_tests.cpp
+++ b/test/vintf_object_tests.cpp
@@ -1399,7 +1399,18 @@
     "</compatibility-matrix>\n",
 };
 
-class KernelTest : public MultiMatrixTest {};
+class KernelTest : public MultiMatrixTest {
+   public:
+    void expectKernelFcmVersion(size_t targetFcm, Level kernelFcm) {
+        std::string xml = "<manifest " + kMetaVersionStr + " type=\"device\" target-level=\"" +
+                          to_string(static_cast<Level>(targetFcm)) + "\">\n";
+        if (kernelFcm != Level::UNSPECIFIED) {
+            xml += "    <kernel target-level=\"" + to_string(kernelFcm) + "\"/>\n";
+        }
+        xml += "</manifest>";
+        expectFetch(kVendorManifest, xml);
+    }
+};
 
 // Assume that we are developing level 2. Test that old <kernel> requirements should
 // not change and new <kernel> versions are added.
@@ -1439,19 +1450,44 @@
     EXPECT_IN(FAKE_KERNEL("4.0.0", "D3", 3), xml) << "\nShould see <kernel> from new matrices";
 }
 
+KernelInfo MakeKernelInfo(const std::string& version, const std::string& key) {
+    KernelInfo info;
+    CHECK(gKernelInfoConverter(&info,
+                               "    <kernel version=\"" + version + "\">\n"
+                               "        <config>\n"
+                               "            <key>CONFIG_" + key + "</key>\n"
+                               "            <value type=\"tristate\">y</value>\n"
+                               "        </config>\n"
+                               "    </kernel>\n"));
+    return info;
+}
+
+TEST_F(KernelTest, Compatible) {
+    setFakeProperties();
+    setupMockFetcher(vendorManifestXml1, systemMatrixXml1, systemManifestXml1, vendorMatrixXml1,
+                     productModel);
+
+    SetUpMockSystemMatrices({
+        "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"1\">\n"
+        FAKE_KERNEL("1.0.0", "A1", 1)
+        FAKE_KERNEL("2.0.0", "B1", 1)
+        "    <sepolicy>\n"
+        "        <kernel-sepolicy-version>0</kernel-sepolicy-version>\n"
+        "        <sepolicy-version>0.0</sepolicy-version>\n"
+        "    </sepolicy>\n"
+        "</compatibility-matrix>\n"});
+    expectKernelFcmVersion(Level{1}, Level{1});
+    expectSystemManifest();
+    expectVendorMatrix();
+
+    auto info = MakeKernelInfo("1.0.0", "A1");
+    runtimeInfoFactory().getInfo()->setNextFetchKernelInfo(info.version(), info.configs());
+    std::string error;
+    ASSERT_EQ(COMPATIBLE, vintfObject->checkCompatibility(&error)) << error;
+}
+
 class KernelTestP : public KernelTest, public WithParamInterface<
-    std::tuple<std::vector<std::string>, KernelInfo, Level, Level, bool>> {
-   public:
-    void expectKernelFcmVersion(size_t targetFcm, Level kernelFcm) {
-        std::string xml = "<manifest " + kMetaVersionStr + " type=\"device\" target-level=\"" +
-                          to_string(static_cast<Level>(targetFcm)) + "\">\n";
-        if (kernelFcm != Level::UNSPECIFIED) {
-            xml += "    <kernel target-level=\"" + to_string(kernelFcm) + "\"/>\n";
-        }
-        xml += "</manifest>";
-        expectFetch(kVendorManifest, xml);
-    }
-};
+    std::tuple<std::vector<std::string>, KernelInfo, Level, Level, bool>> {};
 // Assume that we are developing level 2. Test that old <kernel> requirements should
 // not change and new <kernel> versions are added.
 TEST_P(KernelTestP, Test) {
@@ -1472,17 +1508,6 @@
             << (pass ? error : fallbackError);
 }
 
-KernelInfo MakeKernelInfo(const std::string& version, const std::string& key) {
-    KernelInfo info;
-    CHECK(gKernelInfoConverter(&info,
-                               "    <kernel version=\"" + version + "\">\n"
-                               "        <config>\n"
-                               "            <key>CONFIG_" + key + "</key>\n"
-                               "            <value type=\"tristate\">y</value>\n"
-                               "        </config>\n"
-                               "    </kernel>\n"));
-    return info;
-}
 
 std::vector<KernelTestP::ParamType> KernelTestParamValues() {
     std::vector<KernelTestP::ParamType> ret;