Implement VintfObject::getLatestMinLtsAtFcmVersion.

This function returns the MAX of minlts of FCM at
a given level.

Details can be found in the kernel/configs project.
For example, for Android 12, this returns 5.10.43,
and for Android 13, this returns 5.15.41.

Test: TH
Bug: 255995840

Change-Id: Ib83b8013f4b903da6c943f45c572a15d7b2be997
diff --git a/CompatibilityMatrix.cpp b/CompatibilityMatrix.cpp
index 06f5b73..46218f0 100644
--- a/CompatibilityMatrix.cpp
+++ b/CompatibilityMatrix.cpp
@@ -480,5 +480,18 @@
     return level();
 }
 
+KernelVersion CompatibilityMatrix::getLatestKernelMinLts() const {
+    if (type() != SchemaType::FRAMEWORK) {
+        return {};
+    }
+    auto maxIt = std::max_element(
+        framework.mKernels.begin(), framework.mKernels.end(),
+        [](const MatrixKernel& a, const MatrixKernel& b) { return a.minLts() < b.minLts(); });
+    if (maxIt == framework.mKernels.end()) {
+        return {};
+    }
+    return maxIt->minLts();
+}
+
 } // namespace vintf
 } // namespace android
diff --git a/VintfObject.cpp b/VintfObject.cpp
index bfbc90d..e84486d 100644
--- a/VintfObject.cpp
+++ b/VintfObject.cpp
@@ -1326,6 +1326,28 @@
     return {};
 }
 
+android::base::Result<KernelVersion> VintfObject::getLatestMinLtsAtFcmVersion(Level fcmVersion) {
+    auto allFcms = getAllFrameworkMatrixLevels();
+    if (!allFcms.ok()) return allFcms.error();
+
+    // Get the max of latestKernelMinLts for all FCM fragments at |fcmVersion|.
+    // Usually there's only one such fragment.
+    KernelVersion foundLatestMinLts;
+    for (const auto& fcm : *allFcms) {
+        if (fcm.level() != fcmVersion) {
+            continue;
+        }
+        // Note: this says "minLts", but "Latest" indicates that it is a max value.
+        auto thisLatestMinLts = fcm.getLatestKernelMinLts();
+        if (foundLatestMinLts < thisLatestMinLts) foundLatestMinLts = thisLatestMinLts;
+    }
+    if (foundLatestMinLts != KernelVersion{}) {
+        return foundLatestMinLts;
+    }
+    return android::base::Error(-NAME_NOT_FOUND)
+           << "Can't find compatibility matrix fragment for level " << fcmVersion;
+}
+
 // make_unique does not work because VintfObject constructor is private.
 VintfObject::Builder::Builder()
     : VintfObjectBuilder(std::unique_ptr<VintfObject>(new VintfObject())) {}
diff --git a/include/vintf/CompatibilityMatrix.h b/include/vintf/CompatibilityMatrix.h
index 462e2e9..a5aaa62 100644
--- a/include/vintf/CompatibilityMatrix.h
+++ b/include/vintf/CompatibilityMatrix.h
@@ -152,6 +152,10 @@
     // Prerequisite: matrixKernel is in mKernels.
     Level getSourceMatrixLevel(const MatrixKernel* matrixKernel) const;
 
+    // Return the minlts of the latest <kernel>, or empty value if any error (e.g. this is not an
+    // FCM, or there are no <kernel> tags).
+    [[nodiscard]] KernelVersion getLatestKernelMinLts() const;
+
     friend struct HalManifest;
     friend struct RuntimeInfo;
     friend struct CompatibilityMatrixConverter;
diff --git a/include/vintf/Version.h b/include/vintf/Version.h
index 6c6b0ff..15c1089 100644
--- a/include/vintf/Version.h
+++ b/include/vintf/Version.h
@@ -94,6 +94,9 @@
         if (majorRev > other.majorRev) return false;
         return minorRev < other.minorRev;
     }
+    inline bool operator>(const KernelVersion& other) const { return other < (*this); }
+    inline bool operator<=(const KernelVersion& other) const { return !((*this) > other); }
+    inline bool operator>=(const KernelVersion& other) const { return !((*this) < other); }
 
     inline constexpr Version dropMinor() const { return Version{version, majorRev}; }
 };
diff --git a/include/vintf/VintfObject.h b/include/vintf/VintfObject.h
index 57d92ff..5fded4d 100644
--- a/include/vintf/VintfObject.h
+++ b/include/vintf/VintfObject.h
@@ -269,6 +269,9 @@
         const std::vector<HidlInterfaceMetadata>& hidlMetadata,
         const std::vector<AidlInterfaceMetadata>& aidlMetadata);
 
+    // Get the latest <kernel> minlts for compatibility matrix level |fcmVersion|.
+    android::base::Result<KernelVersion> getLatestMinLtsAtFcmVersion(Level fcmVersion);
+
    private:
     std::unique_ptr<FileSystem> mFileSystem;
     std::unique_ptr<ObjectFactory<RuntimeInfo>> mRuntimeInfoFactory;
diff --git a/test/vintf_object_tests.cpp b/test/vintf_object_tests.cpp
index 37691ed..e84d21e 100644
--- a/test/vintf_object_tests.cpp
+++ b/test/vintf_object_tests.cpp
@@ -39,7 +39,9 @@
 using namespace std::literals;
 
 using android::base::testing::HasError;
+using android::base::testing::HasValue;
 using android::base::testing::Ok;
+using android::base::testing::WithCode;
 using android::base::testing::WithMessage;
 using android::vintf::FqInstance;
 
@@ -2743,6 +2745,56 @@
                          ::testing::ValuesIn(VintfObjectComposerHalTest::GetParams()),
                          [](const auto& info) { return to_string(info.param); });
 
+constexpr const char* systemMatrixLatestMinLtsFormat = R"(
+<compatibility-matrix %s type="framework" level="%s">
+    <kernel version="%s" />
+    <kernel version="%s" />
+    <kernel version="%s" />
+</compatibility-matrix>
+)";
+
+class VintfObjectLatestMinLtsTest : public MultiMatrixTest {};
+
+TEST_F(VintfObjectLatestMinLtsTest, TestEmpty) {
+    SetUpMockSystemMatrices({});
+    EXPECT_THAT(vintfObject->getLatestMinLtsAtFcmVersion(Level::S),
+                HasError(WithCode(-NAME_NOT_FOUND)));
+}
+
+TEST_F(VintfObjectLatestMinLtsTest, TestMissing) {
+    SetUpMockSystemMatrices({
+        android::base::StringPrintf(systemMatrixLatestMinLtsFormat, kMetaVersionStr.c_str(),
+                                    to_string(Level::S).c_str(), "4.19.191", "5.4.86", "5.10.43"),
+    });
+    EXPECT_THAT(
+        vintfObject->getLatestMinLtsAtFcmVersion(Level::T),
+        HasError(WithMessage(HasSubstr("Can't find compatibility matrix fragment for level 7"))));
+}
+
+TEST_F(VintfObjectLatestMinLtsTest, TestSimple) {
+    SetUpMockSystemMatrices({
+        android::base::StringPrintf(systemMatrixLatestMinLtsFormat, kMetaVersionStr.c_str(),
+                                    to_string(Level::S).c_str(), "4.19.191", "5.4.86", "5.10.43"),
+        android::base::StringPrintf(systemMatrixLatestMinLtsFormat, kMetaVersionStr.c_str(),
+                                    to_string(Level::T).c_str(), "5.4.86", "5.10.107", "5.15.41"),
+    });
+    EXPECT_THAT(vintfObject->getLatestMinLtsAtFcmVersion(Level::S),
+                HasValue(KernelVersion{5, 10, 43}));
+    EXPECT_THAT(vintfObject->getLatestMinLtsAtFcmVersion(Level::T),
+                HasValue(KernelVersion{5, 15, 41}));
+}
+
+TEST_F(VintfObjectLatestMinLtsTest, TestMultipleFragment) {
+    SetUpMockSystemMatrices({
+        android::base::StringPrintf(systemMatrixLatestMinLtsFormat, kMetaVersionStr.c_str(),
+                                    to_string(Level::S).c_str(), "4.19.191", "5.4.86", "5.10.43"),
+        android::base::StringPrintf(systemMatrixLatestMinLtsFormat, kMetaVersionStr.c_str(),
+                                    to_string(Level::S).c_str(), "5.4.86", "5.10.107", "5.15.41"),
+    });
+    EXPECT_THAT(vintfObject->getLatestMinLtsAtFcmVersion(Level::S),
+                HasValue(KernelVersion{5, 15, 41}));
+}
+
 }  // namespace testing
 }  // namespace vintf
 }  // namespace android