Allow KernelInfo to be (de)serialized.

Test: libvintf_test
Bug: 111125947
Change-Id: If63fd9354afa1a818f1b866d8d7ac0306bd15cfd
diff --git a/include/vintf/KernelInfo.h b/include/vintf/KernelInfo.h
index 556b768..620883f 100644
--- a/include/vintf/KernelInfo.h
+++ b/include/vintf/KernelInfo.h
@@ -50,6 +50,7 @@
 
    private:
     friend class details::MockRuntimeInfo;
+    friend struct KernelInfoConverter;
     friend struct LibVintfTest;
     friend struct RuntimeInfoFetcher;
     // x.y.z
diff --git a/parse_xml.cpp b/parse_xml.cpp
index 0cc514d..6498012 100644
--- a/parse_xml.cpp
+++ b/parse_xml.cpp
@@ -345,8 +345,9 @@
         return true;
     }
 
-    template <typename T>
-    inline bool parseChildren(NodeType* root, const XmlNodeConverter<T>& conv, std::set<T>* s,
+    template <typename Container, typename T = typename Container::value_type,
+              typename = typename Container::key_compare>
+    inline bool parseChildren(NodeType* root, const XmlNodeConverter<T>& conv, Container* s,
                               std::string* error) const {
         std::vector<T> vec;
         if (!parseChildren(root, conv, &vec, error)) {
@@ -363,6 +364,12 @@
         return true;
     }
 
+    template <typename K, typename V>
+    inline bool parseChildren(NodeType* root, const XmlNodeConverter<std::pair<K, V>>& conv,
+                              std::map<K, V>* s, std::string* error) const {
+        return parseChildren<std::map<K, V>, std::pair<K, V>>(root, conv, s, error);
+    }
+
     inline bool parseText(NodeType* node, std::string* s, std::string* /* error */) const {
         *s = getText(node);
         return true;
@@ -399,14 +406,38 @@
     std::string mElementName;
 };
 
+template <typename Pair>
+struct XmlPairConverter : public XmlNodeConverter<Pair> {
+    XmlPairConverter(
+        const std::string& elementName,
+        std::unique_ptr<XmlNodeConverter<typename Pair::first_type>>&& firstConverter,
+        std::unique_ptr<XmlNodeConverter<typename Pair::second_type>>&& secondConverter)
+        : mElementName(elementName),
+          mFirstConverter(std::move(firstConverter)),
+          mSecondConverter(std::move(secondConverter)) {}
+
+    virtual void mutateNode(const Pair& pair, NodeType* root, DocType* d) const override {
+        appendChild(root, mFirstConverter->serialize(pair.first, d));
+        appendChild(root, mSecondConverter->serialize(pair.second, d));
+    }
+    virtual bool buildObject(Pair* pair, NodeType* root, std::string* error) const override {
+        return this->parseChild(root, *mFirstConverter, &pair->first, error) &&
+               this->parseChild(root, *mSecondConverter, &pair->second, error);
+    }
+    virtual std::string elementName() const { return mElementName; }
+
+   private:
+    std::string mElementName;
+    std::unique_ptr<XmlNodeConverter<typename Pair::first_type>> mFirstConverter;
+    std::unique_ptr<XmlNodeConverter<typename Pair::second_type>> mSecondConverter;
+};
+
 // ---------------------- XmlNodeConverter definitions end
 
 XmlTextConverter<Version> versionConverter{"version"};
 
 XmlTextConverter<VersionRange> versionRangeConverter{"version"};
 
-XmlTextConverter<KernelConfigKey> kernelConfigKeyConverter{"key"};
-
 struct TransportArchConverter : public XmlNodeConverter<TransportArch> {
     std::string elementName() const override { return "transport"; }
     void mutateNode(const TransportArch &object, NodeType *root, DocType *d) const override {
@@ -455,22 +486,9 @@
 
 KernelConfigTypedValueConverter kernelConfigTypedValueConverter{};
 
-struct KernelConfigConverter : public XmlNodeConverter<KernelConfig> {
-    std::string elementName() const override { return "config"; }
-    void mutateNode(const KernelConfig &object, NodeType *root, DocType *d) const override {
-        appendChild(root, kernelConfigKeyConverter(object.first, d));
-        appendChild(root, kernelConfigTypedValueConverter(object.second, d));
-    }
-    bool buildObject(KernelConfig* object, NodeType* root, std::string* error) const override {
-        if (!parseChild(root, kernelConfigKeyConverter, &object->first, error) ||
-            !parseChild(root, kernelConfigTypedValueConverter, &object->second, error)) {
-            return false;
-        }
-        return true;
-    }
-};
-
-KernelConfigConverter kernelConfigConverter{};
+XmlPairConverter<KernelConfig> matrixKernelConfigConverter{
+    "config", std::make_unique<XmlTextConverter<KernelConfigKey>>("key"),
+    std::make_unique<KernelConfigTypedValueConverter>(kernelConfigTypedValueConverter)};
 
 struct HalInterfaceConverter : public XmlNodeConverter<HalInterface> {
     std::string elementName() const override { return "interface"; }
@@ -589,11 +607,11 @@
     std::string elementName() const override { return "conditions"; }
     void mutateNode(const std::vector<KernelConfig>& conds, NodeType* root,
                     DocType* d) const override {
-        appendChildren(root, kernelConfigConverter, conds, d);
+        appendChildren(root, matrixKernelConfigConverter, conds, d);
     }
     bool buildObject(std::vector<KernelConfig>* object, NodeType* root,
                      std::string* error) const override {
-        return parseChildren(root, kernelConfigConverter, object, error);
+        return parseChildren(root, matrixKernelConfigConverter, object, error);
     }
 };
 
@@ -616,14 +634,14 @@
             appendChild(root, matrixKernelConditionsConverter(kernel.mConditions, d));
         }
         if (flags.isKernelConfigsEnabled()) {
-            appendChildren(root, kernelConfigConverter, kernel.mConfigs, d);
+            appendChildren(root, matrixKernelConfigConverter, kernel.mConfigs, d);
         }
     }
     bool buildObject(MatrixKernel* object, NodeType* root, std::string* error) const override {
         if (!parseAttr(root, "version", &object->mMinLts, error) ||
             !parseOptionalChild(root, matrixKernelConditionsConverter, {}, &object->mConditions,
                                 error) ||
-            !parseChildren(root, kernelConfigConverter, &object->mConfigs, error)) {
+            !parseChildren(root, matrixKernelConfigConverter, &object->mConfigs, error)) {
             return false;
         }
         return true;
@@ -858,6 +876,32 @@
 };
 ManifestXmlFileConverter manifestXmlFileConverter{};
 
+XmlPairConverter<std::pair<std::string, std::string>> kernelConfigConverter{
+    "config", std::make_unique<XmlTextConverter<std::string>>("key"),
+    std::make_unique<XmlTextConverter<std::string>>("value")};
+
+struct KernelInfoConverter : public XmlNodeConverter<KernelInfo> {
+    std::string elementName() const override { return "kernel"; }
+    void mutateNode(const KernelInfo& o, NodeType* root, DocType* d) const override {
+        mutateNode(o, root, d, SerializeFlags::EVERYTHING);
+    }
+    void mutateNode(const KernelInfo& o, NodeType* root, DocType* d,
+                    SerializeFlags::Type flags) const override {
+        if (o.version() != KernelVersion{}) {
+            appendAttr(root, "version", o.version());
+        }
+        if (flags.isKernelConfigsEnabled()) {
+            appendChildren(root, kernelConfigConverter, o.configs(), d);
+        }
+    }
+    bool buildObject(KernelInfo* o, NodeType* root, std::string* error) const override {
+        return parseOptionalAttr(root, "version", {}, &o->mVersion, error) &&
+               parseChildren(root, kernelConfigConverter, &o->mConfigs, error);
+    }
+};
+
+KernelInfoConverter kernelInfoConverter{};
+
 struct HalManifestConverter : public XmlNodeConverter<HalManifest> {
     std::string elementName() const override { return "manifest"; }
     void mutateNode(const HalManifest &m, NodeType *root, DocType *d) const override {
@@ -1189,6 +1233,7 @@
     kernelConfigTypedValueConverter;
 XmlConverter<MatrixHal>& gMatrixHalConverter = matrixHalConverter;
 XmlConverter<ManifestHal>& gManifestHalConverter = manifestHalConverter;
+XmlConverter<KernelInfo>& gKernelInfoConverter = kernelInfoConverter;
 
 } // namespace vintf
 } // namespace android
diff --git a/test/LibVintfTest.cpp b/test/LibVintfTest.cpp
index afd602e..f5690aa 100644
--- a/test/LibVintfTest.cpp
+++ b/test/LibVintfTest.cpp
@@ -37,6 +37,7 @@
 extern XmlConverter<ManifestHal>& gManifestHalConverter;
 extern XmlConverter<MatrixHal>& gMatrixHalConverter;
 extern XmlConverter<KernelConfigTypedValue>& gKernelConfigTypedValueConverter;
+extern XmlConverter<KernelInfo>& gKernelInfoConverter;
 extern XmlConverter<HalManifest>& gHalManifestConverter;
 extern XmlConverter<CompatibilityMatrix>& gCompatibilityMatrixConverter;
 
@@ -186,15 +187,20 @@
         info.mOsVersion = "#4 SMP PREEMPT Wed Feb 1 18:10:52 PST 2017";
         info.mHardwareId = "aarch64";
         info.mKernelSepolicyVersion = 30;
-        info.mKernel.mVersion = {3, 18, 31};
-        info.mKernel.mConfigs = {{"CONFIG_64BIT", "y"},
-                                 {"CONFIG_ANDROID_BINDER_DEVICES", "\"binder,hwbinder\""},
-                                 {"CONFIG_ARCH_MMAP_RND_BITS", "24"},
-                                 {"CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES", "\"\""},
-                                 {"CONFIG_ILLEGAL_POINTER_VALUE", "0xdead000000000000"}};
+        info.mKernel = testKernelInfo();
         setAvb(info, {2, 1}, {2, 1});
         return info;
     }
+    KernelInfo testKernelInfo() {
+        KernelInfo info;
+        info.mVersion = {3, 18, 31};
+        info.mConfigs = {{"CONFIG_64BIT", "y"},
+                         {"CONFIG_ANDROID_BINDER_DEVICES", "\"binder,hwbinder\""},
+                         {"CONFIG_ARCH_MMAP_RND_BITS", "24"},
+                         {"CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES", "\"\""},
+                         {"CONFIG_ILLEGAL_POINTER_VALUE", "0xdead000000000000"}};
+        return info;
+    }
 };
 
 TEST_F(LibVintfTest, ArchOperatorOr) {
@@ -3440,6 +3446,35 @@
               std::set<std::string>({"android.hardware.camera@2.0", "android.hardware.nfc@1.0"}));
 }
 
+TEST_F(LibVintfTest, KernelInfo) {
+    KernelInfo ki = testKernelInfo();
+
+    EXPECT_EQ(
+        "<kernel version=\"3.18.31\">\n"
+        "    <config>\n"
+        "        <key>CONFIG_64BIT</key>\n"
+        "        <value>y</value>\n"
+        "    </config>\n"
+        "    <config>\n"
+        "        <key>CONFIG_ANDROID_BINDER_DEVICES</key>\n"
+        "        <value>\"binder,hwbinder\"</value>\n"
+        "    </config>\n"
+        "    <config>\n"
+        "        <key>CONFIG_ARCH_MMAP_RND_BITS</key>\n"
+        "        <value>24</value>\n"
+        "    </config>\n"
+        "    <config>\n"
+        "        <key>CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES</key>\n"
+        "        <value>\"\"</value>\n"
+        "    </config>\n"
+        "    <config>\n"
+        "        <key>CONFIG_ILLEGAL_POINTER_VALUE</key>\n"
+        "        <value>0xdead000000000000</value>\n"
+        "    </config>\n"
+        "</kernel>\n",
+        gKernelInfoConverter(ki, SerializeFlags::NO_TAGS.enableKernelConfigs()));
+}
+
 } // namespace vintf
 } // namespace android