summaryrefslogblamecommitdiffstats
blob: 3df7e19e0ece0a5e4c5ef80f9a1ff894e6026286 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

















                                                                           






                                 

                     
                      
                         
















                                                        
                                                           




































                                                                                              
                                           
                                                    


                                            
                                                          





























                                                                                       












                                                            








                                                                                


                                                                                             




                                                                      

                                                                         
                                                            
                                            

                    
                                                                               
                                        
                                                   











                                                                      




                                                     





                                                                    

                                                                                








                                                                     

                                                                                 








                                                                                           


























                                                                                 














                                                                                         




                                                                                                 

                                                                                                  






                                                                                                 

                                                                                           



                   



                                                                   

                                                                                              





                            






                                                                                            









                                                                                 



                                                                                         

                                                                                                     









                                                  














                                                                                   




                                                                                                         

                                                                                            





                             
















                                                                                                      




                                                                 

                                                       





                                                                                                    
     












                                                                                              
                                             








                                                                     

                                                                      
 

                                                                        
























                                                                                                 












                                                                                                      
                                                                                             






                                                                        

















                                                                                            























                                                                                          
                                                                




                                                                                      
                                                                          
                                                                                      

                                                                        
                                             

                                                                                                
                                                             

                                                                                  

                         



                                                                                         


                                                                                             


                             


































                                                                                               

                    
      


                                              
 












                                                                                             
                                                                      

                                                                                            
                                                    


                                                                                      
                                                                        

                                                                           
                                                            
                                                                                                   
                                                                             

                         




                                                    
 
                                                                    



                                                                                        
                                                                        
                                                                
                                                                                      

                                                                          
                                             
                                                                                   
                                                             
                                                                                            
                                                                        
                                                                       

                         























                                                                                                 




                                                                        


                                                                                             


                             



                                                                                     






















                                                                                     
                    
     
      

  

                                                                             
                                                  
 
                                                                                                        
                                                                                  
 
                                                              
                                                                   

                                                                                             
                                                                                     
     

                                                                                                 
                                                                                              

                         



                                            
 



















                                                                                    










                                                                                  



















                                                                                          
                                                                    
                                                                   
                                                                                      



                                                                     
                                                        
                                          


                                                             



                                                                       
                                            


                                                                                              
                                                      


                                                                           
         
 


                                                                               
     
                                                                          
                                      
                                                                 
                                                       
                                                                                            


                                                                


                                                                                                   

                         

                                                  

                                                                                                   



                                                                           










                                                                                      
         
                                 
                                              
                                               
                                                                                  

                             
         













                                                                                       



                    
                                                  
 











                                                                                  























                                                                                        
                                                                                    

                                                                                              



                                                                             
                                                               
                                          


                                                      



                                                                                
                                               








                                                                                     
                                                   


                                                                    
         
 


                                                                             

                                                                                  
                        
                                    

                                                                                                




                                                              

                                                                                                  
                                                                                           
                                                                                                 
                                                                                                  

                             














                                                                                                    
                                                         


                                                                                               

                             

         


                                                                                                  


                                 



                                                                               
         

















                                                                                                  




                                                                  

                                       
                                                                              


                                                                      




                                                                            
                                                                              
 

                      
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Convert objects from and to xml.

#define LOG_TAG "libvintf"
#include <android-base/logging.h>

#include "parse_xml.h"

#include <type_traits>

#include <tinyxml2.h>

#include "constants.h"
#include "parse_string.h"

namespace android {
namespace vintf {

// --------------- tinyxml2 details

using NodeType = tinyxml2::XMLElement;
using DocType = tinyxml2::XMLDocument;

// caller is responsible for deleteDocument() call
inline DocType *createDocument() {
    return new tinyxml2::XMLDocument();
}

// caller is responsible for deleteDocument() call
inline DocType *createDocument(const std::string &xml) {
    DocType *doc = new tinyxml2::XMLDocument();
    if (doc->Parse(xml.c_str()) == tinyxml2::XML_SUCCESS) {
        return doc;
    }
    delete doc;
    return nullptr;
}

inline void deleteDocument(DocType *d) {
    delete d;
}

inline std::string printDocument(DocType *d) {
    tinyxml2::XMLPrinter p;
    d->Print(&p);
    return std::string{p.CStr()};
}

inline NodeType *createNode(const std::string &name, DocType *d) {
    return d->NewElement(name.c_str());
}

inline void appendChild(NodeType *parent, NodeType *child) {
    parent->InsertEndChild(child);
}

inline void appendChild(DocType *parent, NodeType *child) {
    parent->InsertEndChild(child);
}

inline void appendStrAttr(NodeType *e, const std::string &attrName, const std::string &attr) {
    e->SetAttribute(attrName.c_str(), attr.c_str());
}

// text -> text
inline void appendText(NodeType *parent, const std::string &text, DocType *d) {
    parent->InsertEndChild(d->NewText(text.c_str()));
}

inline std::string nameOf(NodeType *root) {
    return root->Name() == NULL ? "" : root->Name();
}

inline std::string getText(NodeType *root) {
    return root->GetText() == NULL ? "" : root->GetText();
}

inline NodeType *getChild(NodeType *parent, const std::string &name) {
    return parent->FirstChildElement(name.c_str());
}

inline NodeType *getRootChild(DocType *parent) {
    return parent->FirstChildElement();
}

inline std::vector<NodeType *> getChildren(NodeType *parent, const std::string &name) {
    std::vector<NodeType *> v;
    for (NodeType *child = parent->FirstChildElement(name.c_str());
         child != nullptr;
         child = child->NextSiblingElement(name.c_str())) {
        v.push_back(child);
    }
    return v;
}

inline bool getAttr(NodeType *root, const std::string &attrName, std::string *s) {
    const char *c = root->Attribute(attrName.c_str());
    if (c == NULL)
        return false;
    *s = c;
    return true;
}

// --------------- tinyxml2 details end.

// Helper functions for XmlConverter
static bool parse(const std::string &attrText, bool *attr) {
    if (attrText == "true" || attrText == "1") {
        *attr = true;
        return true;
    }
    if (attrText == "false" || attrText == "0") {
        *attr = false;
        return true;
    }
    return false;
}

// ---------------------- XmlNodeConverter definitions

template<typename Object>
struct XmlNodeConverter : public XmlConverter<Object> {
    XmlNodeConverter() {}
    virtual ~XmlNodeConverter() {}

    // sub-types should implement these.
    virtual void mutateNode(const Object &o, NodeType *n, DocType *d) const = 0;
    virtual void mutateNode(const Object& o, NodeType* n, DocType* d, SerializeFlags) const {
        mutateNode(o, n, d);
    }
    virtual bool buildObject(Object *o, NodeType *n) const = 0;
    virtual std::string elementName() const = 0;

    // convenience methods for user
    inline const std::string &lastError() const { return mLastError; }
    inline NodeType* serialize(const Object& o, DocType* d,
                               SerializeFlags flags = EVERYTHING) const {
        NodeType *root = createNode(this->elementName(), d);
        this->mutateNode(o, root, d, flags);
        return root;
    }
    inline std::string serialize(const Object& o, SerializeFlags flags) const {
        DocType *doc = createDocument();
        appendChild(doc, serialize(o, doc, flags));
        std::string s = printDocument(doc);
        deleteDocument(doc);
        return s;
    }
    inline bool deserialize(Object *object, NodeType *root) const {
        if (nameOf(root) != this->elementName()) {
            return false;
        }
        return this->buildObject(object, root);
    }
    inline bool deserialize(Object *o, const std::string &xml) const {
        DocType *doc = createDocument(xml);
        if (doc == nullptr) {
            this->mLastError = "Not a valid XML";
            return false;
        }
        bool ret = deserialize(o, getRootChild(doc));
        deleteDocument(doc);
        return ret;
    }
    inline NodeType *operator()(const Object &o, DocType *d) const {
        return serialize(o, d);
    }
    inline std::string operator()(const Object& o, SerializeFlags flags) const {
        return serialize(o, flags);
    }
    inline bool operator()(Object *o, NodeType *node) const {
        return deserialize(o, node);
    }
    inline bool operator()(Object *o, const std::string &xml) const {
        return deserialize(o, xml);
    }

    // convenience methods for implementor.

    // All append* functions helps mutateNode() to serialize the object into XML.
    template <typename T>
    inline void appendAttr(NodeType *e, const std::string &attrName, const T &attr) const {
        return appendStrAttr(e, attrName, ::android::vintf::to_string(attr));
    }

    inline void appendAttr(NodeType *e, const std::string &attrName, bool attr) const {
        return appendStrAttr(e, attrName, attr ? "true" : "false");
    }

    // text -> <name>text</name>
    inline void appendTextElement(NodeType *parent, const std::string &name,
                const std::string &text, DocType *d) const {
        NodeType *c = createNode(name, d);
        appendText(c, text, d);
        appendChild(parent, c);
    }

    // text -> <name>text</name>
    template<typename Array>
    inline void appendTextElements(NodeType *parent, const std::string &name,
                const Array &array, DocType *d) const {
        for (const std::string &text : array) {
            NodeType *c = createNode(name, d);
            appendText(c, text, d);
            appendChild(parent, c);
        }
    }

    template<typename T, typename Array>
    inline void appendChildren(NodeType *parent, const XmlNodeConverter<T> &conv,
            const Array &array, DocType *d) const {
        for (const T &t : array) {
            appendChild(parent, conv(t, d));
        }
    }

    // All parse* functions helps buildObject() to deserialize XML to the object. Returns
    // true if deserialization is successful, false if any error, and mLastError will be
    // set to error message.
    template <typename T>
    inline bool parseOptionalAttr(NodeType *root, const std::string &attrName,
            T &&defaultValue, T *attr) const {
        std::string attrText;
        bool success = getAttr(root, attrName, &attrText) &&
                       ::android::vintf::parse(attrText, attr);
        if (!success) {
            *attr = std::move(defaultValue);
        }
        return true;
    }

    template <typename T>
    inline bool parseAttr(NodeType *root, const std::string &attrName, T *attr) const {
        std::string attrText;
        bool ret = getAttr(root, attrName, &attrText) && ::android::vintf::parse(attrText, attr);
        if (!ret) {
            mLastError = "Could not find/parse attr with name \"" + attrName + "\" and value \"" +
                         attrText + "\" for element <" + elementName() + ">";
        }
        return ret;
    }

    inline bool parseAttr(NodeType *root, const std::string &attrName, std::string *attr) const {
        bool ret = getAttr(root, attrName, attr);
        if (!ret) {
            mLastError = "Could not find attr with name \"" + attrName + "\" for element <"
                    + elementName() + ">";
        }
        return ret;
    }

    inline bool parseTextElement(NodeType *root,
            const std::string &elementName, std::string *s) const {
        NodeType *child = getChild(root, elementName);
        if (child == nullptr) {
            mLastError = "Could not find element with name <" + elementName + "> in element <"
                    + this->elementName() + ">";
            return false;
        }
        *s = getText(child);
        return true;
    }

    inline bool parseOptionalTextElement(NodeType* root, const std::string& elementName,
                                         std::string&& defaultValue, std::string* s) const {
        NodeType* child = getChild(root, elementName);
        *s = child == nullptr ? std::move(defaultValue) : getText(child);
        return true;
    }

    inline bool parseTextElements(NodeType *root, const std::string &elementName,
            std::vector<std::string> *v) const {
        auto nodes = getChildren(root, elementName);
        v->resize(nodes.size());
        for (size_t i = 0; i < nodes.size(); ++i) {
            v->at(i) = getText(nodes[i]);
        }
        return true;
    }

    template <typename T>
    inline bool parseChild(NodeType *root, const XmlNodeConverter<T> &conv, T *t) const {
        NodeType *child = getChild(root, conv.elementName());
        if (child == nullptr) {
            mLastError = "Could not find element with name <" + conv.elementName() + "> in element <"
                    + this->elementName() + ">";
            return false;
        }
        bool success = conv.deserialize(t, child);
        if (!success) {
            mLastError = conv.lastError();
        }
        return success;
    }

    template <typename T>
    inline bool parseOptionalChild(NodeType *root, const XmlNodeConverter<T> &conv,
            T &&defaultValue, T *t) const {
        NodeType *child = getChild(root, conv.elementName());
        if (child == nullptr) {
            *t = std::move(defaultValue);
            return true;
        }
        bool success = conv.deserialize(t, child);
        if (!success) {
            mLastError = conv.lastError();
        }
        return success;
    }

    template <typename T>
    inline bool parseChildren(NodeType *root, const XmlNodeConverter<T> &conv, std::vector<T> *v) const {
        auto nodes = getChildren(root, conv.elementName());
        v->resize(nodes.size());
        for (size_t i = 0; i < nodes.size(); ++i) {
            if (!conv.deserialize(&v->at(i), nodes[i])) {
                mLastError = "Could not parse element with name <" + conv.elementName()
                        + "> in element <" + this->elementName() + ">: " + conv.lastError();
                return false;
            }
        }
        return true;
    }

    template <typename T>
    inline bool parseChildren(NodeType *root, const XmlNodeConverter<T> &conv, std::set<T> *s) const {
        std::vector<T> vec;
        if (!parseChildren(root, conv, &vec)) {
            return false;
        }
        s->clear();
        s->insert(vec.begin(), vec.end());
        if (s->size() != vec.size()) {
            mLastError = "Duplicated elements <" + conv.elementName() + "> in element <"
                    + this->elementName() + ">";
            s->clear();
            return false;
        }
        return true;
    }

    inline bool parseText(NodeType *node, std::string *s) const {
        *s = getText(node);
        return true;
    }

    template <typename T>
    inline bool parseText(NodeType *node, T *s) const {
        std::string text = getText(node);
        bool ret = ::android::vintf::parse(text, s);
        if (!ret) {
            mLastError = "Could not parse text \"" + text + "\" in element <" + elementName() + ">";
        }
        return ret;
    }
protected:
    mutable std::string mLastError;
};

template<typename Object>
struct XmlTextConverter : public XmlNodeConverter<Object> {
    XmlTextConverter(const std::string &elementName)
        : mElementName(elementName) {}

    virtual void mutateNode(const Object &object, NodeType *root, DocType *d) const override {
        appendText(root, ::android::vintf::to_string(object), d);
    }
    virtual bool buildObject(Object *object, NodeType *root) const override {
        return this->parseText(root, object);
    }
    virtual std::string elementName() const { return mElementName; };
private:
    std::string mElementName;
};

// ---------------------- XmlNodeConverter definitions end

const XmlTextConverter<Version> versionConverter{"version"};

const XmlTextConverter<VersionRange> versionRangeConverter{"version"};

const 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 {
        if (object.arch != Arch::ARCH_EMPTY) {
            appendAttr(root, "arch", object.arch);
        }
        appendText(root, ::android::vintf::to_string(object.transport), d);
    }
    bool buildObject(TransportArch *object, NodeType *root) const override {
        if (!parseOptionalAttr(root, "arch", Arch::ARCH_EMPTY, &object->arch) ||
            !parseText(root, &object->transport)) {
            return false;
        }
        if (!object->isValid()) {
            this->mLastError = "transport == " + ::android::vintf::to_string(object->transport) +
                    " and arch == " + ::android::vintf::to_string(object->arch) +
                    " is not a valid combination.";
            return false;
        }
        return true;
    }
};

const TransportArchConverter transportArchConverter{};

struct KernelConfigTypedValueConverter : public XmlNodeConverter<KernelConfigTypedValue> {
    std::string elementName() const override { return "value"; }
    void mutateNode(const KernelConfigTypedValue &object, NodeType *root, DocType *d) const override {
        appendAttr(root, "type", object.mType);
        appendText(root, ::android::vintf::to_string(object), d);
    }
    bool buildObject(KernelConfigTypedValue *object, NodeType *root) const override {
        std::string stringValue;
        if (!parseAttr(root, "type", &object->mType) ||
            !parseText(root, &stringValue)) {
            return false;
        }
        if (!::android::vintf::parseKernelConfigValue(stringValue, object)) {
            this->mLastError = "Could not parse kernel config value \"" + stringValue + "\"";
            return false;
        }
        return true;
    }
};

const 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) const override {
        if (   !parseChild(root, kernelConfigKeyConverter, &object->first)
            || !parseChild(root, kernelConfigTypedValueConverter, &object->second)) {
            return false;
        }
        return true;
    }
};

const KernelConfigConverter kernelConfigConverter{};

struct HalInterfaceConverter : public XmlNodeConverter<HalInterface> {
    std::string elementName() const override { return "interface"; }
    void mutateNode(const HalInterface &intf, NodeType *root, DocType *d) const override {
        appendTextElement(root, "name", intf.name, d);
        appendTextElements(root, "instance", intf.instances, d);
    }
    bool buildObject(HalInterface *intf, NodeType *root) const override {
        std::vector<std::string> instances;
        if (!parseTextElement(root, "name", &intf->name) ||
            !parseTextElements(root, "instance", &instances)) {
            return false;
        }
        intf->instances.clear();
        intf->instances.insert(instances.begin(), instances.end());
        if (intf->instances.size() != instances.size()) {
            this->mLastError = "Duplicated instances in " + intf->name;
            return false;
        }
        return true;
    }
};

const HalInterfaceConverter halInterfaceConverter{};

struct MatrixHalConverter : public XmlNodeConverter<MatrixHal> {
    std::string elementName() const override { return "hal"; }
    void mutateNode(const MatrixHal &hal, NodeType *root, DocType *d) const override {
        appendAttr(root, "format", hal.format);
        appendAttr(root, "optional", hal.optional);
        appendTextElement(root, "name", hal.name, d);
        appendChildren(root, versionRangeConverter, hal.versionRanges, d);
        appendChildren(root, halInterfaceConverter, iterateValues(hal.interfaces), d);
    }
    bool buildObject(MatrixHal *object, NodeType *root) const override {
        std::vector<HalInterface> interfaces;
        if (!parseOptionalAttr(root, "format", HalFormat::HIDL, &object->format) ||
            !parseOptionalAttr(root, "optional", false /* defaultValue */, &object->optional) ||
            !parseTextElement(root, "name", &object->name) ||
            !parseChildren(root, versionRangeConverter, &object->versionRanges) ||
            !parseChildren(root, halInterfaceConverter, &interfaces)) {
            return false;
        }
        for (auto&& interface : interfaces) {
            std::string name{interface.name};
            auto res = object->interfaces.emplace(std::move(name), std::move(interface));
            if (!res.second) {
                this->mLastError = "Duplicated interface entry \"" + res.first->first +
                                   "\"; if additional instances are needed, add them to the "
                                   "existing <interface> node.";
                return false;
            }
        }
// Do not check for target-side libvintf to avoid restricting ability for upgrade accidentally.
#ifdef LIBVINTF_HOST
        if (!checkAdditionalRestrictionsOnHal(*object)) {
            return false;
        }
#endif
        return true;
    }

#ifdef LIBVINTF_HOST
   private:
    bool checkAdditionalRestrictionsOnHal(const MatrixHal& hal) const {
        if (hal.getName() == "netutils-wrapper") {
            if (hal.versionRanges.size() != 1) {
                this->mLastError =
                    "netutils-wrapper HAL must specify exactly one version x.0, "
                    "but multiple <version> element is specified.";
                return false;
            }
            const VersionRange& v = hal.versionRanges.at(0);
            if (!v.isSingleVersion()) {
                this->mLastError =
                    "netutils-wrapper HAL must specify exactly one version x.0, "
                    "but a range is provided. Perhaps you mean '" +
                    to_string(Version{v.majorVer, 0}) + "'?";
                return false;
            }
            if (v.minMinor != 0) {
                this->mLastError =
                    "netutils-wrapper HAL must specify exactly one version x.0, "
                    "but minor version is not 0. Perhaps you mean '" +
                    to_string(Version{v.majorVer, 0}) + "'?";
                return false;
            }
        }
        return true;
    }
#endif
};

const MatrixHalConverter matrixHalConverter{};

struct MatrixKernelConditionsConverter : public XmlNodeConverter<std::vector<KernelConfig>> {
    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);
    }
    bool buildObject(std::vector<KernelConfig>* object, NodeType* root) const override {
        return parseChildren(root, kernelConfigConverter, object);
    }
};

const MatrixKernelConditionsConverter matrixKernelConditionsConverter{};

struct MatrixKernelConverter : public XmlNodeConverter<MatrixKernel> {
    std::string elementName() const override { return "kernel"; }
    void mutateNode(const MatrixKernel &kernel, NodeType *root, DocType *d) const override {
        appendAttr(root, "version", kernel.mMinLts);
        if (!kernel.mConditions.empty()) {
            appendChild(root, matrixKernelConditionsConverter(kernel.mConditions, d));
        }
        appendChildren(root, kernelConfigConverter, kernel.mConfigs, d);
    }
    bool buildObject(MatrixKernel *object, NodeType *root) const override {
        if (!parseAttr(root, "version", &object->mMinLts) ||
            !parseOptionalChild(root, matrixKernelConditionsConverter, {}, &object->mConditions) ||
            !parseChildren(root, kernelConfigConverter, &object->mConfigs)) {
            return false;
        }
        return true;
    }
};

const MatrixKernelConverter matrixKernelConverter{};

struct ManifestHalConverter : public XmlNodeConverter<ManifestHal> {
    std::string elementName() const override { return "hal"; }
    void mutateNode(const ManifestHal &hal, NodeType *root, DocType *d) const override {
        appendAttr(root, "format", hal.format);
        appendTextElement(root, "name", hal.name, d);
        appendChild(root, transportArchConverter(hal.transportArch, d));
        appendChildren(root, versionConverter, hal.versions, d);
        appendChildren(root, halInterfaceConverter, iterateValues(hal.interfaces), d);
    }
    bool buildObject(ManifestHal *object, NodeType *root) const override {
        std::vector<HalInterface> interfaces;
        if (!parseOptionalAttr(root, "format", HalFormat::HIDL, &object->format) ||
            !parseTextElement(root, "name", &object->name) ||
            !parseOptionalChild(root, transportArchConverter, {}, &object->transportArch) ||
            !parseChildren(root, versionConverter, &object->versions) ||
            !parseChildren(root, halInterfaceConverter, &interfaces)) {
            return false;
        }

        switch (object->format) {
            case HalFormat::HIDL: {
                if (object->transportArch.empty()) {
                    this->mLastError =
                        "HIDL HAL '" + object->name + "' should have <transport> defined.";
                    return false;
                }
            } break;
            case HalFormat::NATIVE: {
                if (!object->transportArch.empty()) {
                    this->mLastError =
                        "Native HAL '" + object->name + "' should not have <transport> defined.";
                    return false;
                }
            } break;
            default: {
                LOG(FATAL) << "Unhandled HalFormat "
                           << static_cast<typename std::underlying_type<HalFormat>::type>(
                                  object->format);
            } break;
        }
        if (!object->transportArch.isValid()) return false;

        object->interfaces.clear();
        for (auto &&interface : interfaces) {
            auto res = object->interfaces.emplace(interface.name,
                                                  std::move(interface));
            if (!res.second) {
                this->mLastError = "Duplicated interface entry \"" + res.first->first +
                                   "\"; if additional instances are needed, add them to the "
                                   "existing <interface> node.";
                return false;
            }
        }
        if (!object->isValid()) {
            this->mLastError = "'" + object->name + "' is not a valid Manifest HAL.";
            return false;
        }
// Do not check for target-side libvintf to avoid restricting upgrade accidentally.
#ifdef LIBVINTF_HOST
        if (!checkAdditionalRestrictionsOnHal(*object)) {
            return false;
        }
#endif
        return true;
    }

#ifdef LIBVINTF_HOST
   private:
    bool checkAdditionalRestrictionsOnHal(const ManifestHal& hal) const {
        if (hal.getName() == "netutils-wrapper") {
            for (const Version& v : hal.versions) {
                if (v.minorVer != 0) {
                    this->mLastError =
                        "netutils-wrapper HAL must specify exactly one version x.0, "
                        "but minor version is not 0. Perhaps you mean '" +
                        to_string(Version{v.majorVer, 0}) + "'?";
                    return false;
                }
            }
        }
        return true;
    }
#endif
};

// Convert ManifestHal from and to XML. Returned object is guaranteed to have
// .isValid() == true.
const ManifestHalConverter manifestHalConverter{};

const XmlTextConverter<KernelSepolicyVersion> kernelSepolicyVersionConverter{"kernel-sepolicy-version"};
const XmlTextConverter<VersionRange> sepolicyVersionConverter{"sepolicy-version"};

struct SepolicyConverter : public XmlNodeConverter<Sepolicy> {
    std::string elementName() const override { return "sepolicy"; }
    void mutateNode(const Sepolicy &object, NodeType *root, DocType *d) const override {
        appendChild(root, kernelSepolicyVersionConverter(object.kernelSepolicyVersion(), d));
        appendChildren(root, sepolicyVersionConverter, object.sepolicyVersions(), d);
    }
    bool buildObject(Sepolicy *object, NodeType *root) const override {
        if (!parseChild(root, kernelSepolicyVersionConverter, &object->mKernelSepolicyVersion) ||
            !parseChildren(root, sepolicyVersionConverter, &object->mSepolicyVersionRanges)) {
            return false;
        }
        return true;
    }
};
const SepolicyConverter sepolicyConverter{};

const XmlTextConverter<VndkVersionRange> vndkVersionRangeConverter{"version"};
const XmlTextConverter<std::string> vndkLibraryConverter{"library"};

struct VndkConverter : public XmlNodeConverter<Vndk> {
    std::string elementName() const override { return "vndk"; }
    void mutateNode(const Vndk &object, NodeType *root, DocType *d) const override {
        appendChild(root, vndkVersionRangeConverter(object.mVersionRange, d));
        appendChildren(root, vndkLibraryConverter, object.mLibraries, d);
    }
    bool buildObject(Vndk *object, NodeType *root) const override {
        if (!parseChild(root, vndkVersionRangeConverter, &object->mVersionRange) ||
            !parseChildren(root, vndkLibraryConverter, &object->mLibraries)) {
            return false;
        }
        return true;
    }
};

const VndkConverter vndkConverter{};

struct HalManifestSepolicyConverter : public XmlNodeConverter<Version> {
    std::string elementName() const override { return "sepolicy"; }
    void mutateNode(const Version &m, NodeType *root, DocType *d) const override {
        appendChild(root, versionConverter(m, d));
    }
    bool buildObject(Version *object, NodeType *root) const override {
        return parseChild(root, versionConverter, object);
    }
};
const HalManifestSepolicyConverter halManifestSepolicyConverter{};

struct ManifestXmlFileConverter : public XmlNodeConverter<ManifestXmlFile> {
    std::string elementName() const override { return "xmlfile"; }
    void mutateNode(const ManifestXmlFile& f, NodeType* root, DocType* d) const override {
        appendTextElement(root, "name", f.name(), d);
        appendChild(root, versionConverter(f.version(), d));
        if (!f.overriddenPath().empty()) {
            appendTextElement(root, "path", f.overriddenPath(), d);
        }
    }
    bool buildObject(ManifestXmlFile* object, NodeType* root) const override {
        if (!parseTextElement(root, "name", &object->mName) ||
            !parseChild(root, versionConverter, &object->mVersion) ||
            !parseOptionalTextElement(root, "path", {}, &object->mOverriddenPath)) {
            return false;
        }
        return true;
    }
};
const ManifestXmlFileConverter manifestXmlFileConverter{};

struct HalManifestConverter : public XmlNodeConverter<HalManifest> {
    std::string elementName() const override { return "manifest"; }
    void mutateNode(const HalManifest &m, NodeType *root, DocType *d) const override {
        mutateNode(m, root, d, SerializeFlag::EVERYTHING);
    }
    void mutateNode(const HalManifest& m, NodeType* root, DocType* d,
                    SerializeFlags flags) const override {
        appendAttr(root, "version", m.getMetaVersion());
        appendAttr(root, "type", m.mType);
        if (m.mLevel != Level::UNSPECIFIED) {
            this->appendAttr(root, "target-level", m.mLevel);
        }

        if (!(flags & SerializeFlag::NO_HALS)) {
            appendChildren(root, manifestHalConverter, m.getHals(), d);
        }
        if (m.mType == SchemaType::DEVICE) {
            if (!(flags & SerializeFlag::NO_SEPOLICY)) {
                appendChild(root, halManifestSepolicyConverter(m.device.mSepolicyVersion, d));
            }
        } else if (m.mType == SchemaType::FRAMEWORK) {
            if (!(flags & SerializeFlag::NO_VNDK)) {
                appendChildren(root, vndkConverter, m.framework.mVndks, d);
            }
        }

        if (!(flags & SerializeFlag::NO_XMLFILES)) {
            appendChildren(root, manifestXmlFileConverter, m.getXmlFiles(), d);
        }
    }
    bool buildObject(HalManifest *object, NodeType *root) const override {
        std::vector<ManifestHal> hals;
        if (!parseAttr(root, "version", &object->mMetaVersion) ||
            !parseAttr(root, "type", &object->mType) ||
            !parseOptionalAttr(root, "target-level", Level::UNSPECIFIED, &object->mLevel) ||
            !parseChildren(root, manifestHalConverter, &hals)) {
            return false;
        }
        if (!kMetaVersion.minorAtLeast(object->mMetaVersion)) {
            this->mLastError = "Unrecognized manifest.version " + to_string(object->mMetaVersion) +
                               " (libvintf@" + to_string(kMetaVersion) + ")";
            return false;
        }
        if (object->mType == SchemaType::DEVICE) {
            // tags for device hal manifest only.
            // <sepolicy> can be missing because it can be determined at build time, not hard-coded
            // in the XML file.
            if (!parseOptionalChild(root, halManifestSepolicyConverter, {},
                    &object->device.mSepolicyVersion)) {
                return false;
            }
        } else if (object->mType == SchemaType::FRAMEWORK) {
            if (!parseChildren(root, vndkConverter, &object->framework.mVndks)) {
                return false;
            }
            for (const auto &vndk : object->framework.mVndks) {
                if (!vndk.mVersionRange.isSingleVersion()) {
                    this->mLastError = "vndk.version " + to_string(vndk.mVersionRange)
                            + " cannot be a range for manifests";
                    return false;
                }
            }
        }
        for (auto &&hal : hals) {
            std::string description{hal.name};
            if (!object->add(std::move(hal))) {
                this->mLastError = "Duplicated manifest.hal entry " + description;
                return false;
            }
        }

        std::vector<ManifestXmlFile> xmlFiles;
        if (!parseChildren(root, manifestXmlFileConverter, &xmlFiles)) {
            return false;
        }
        for (auto&& xmlFile : xmlFiles) {
            std::string description{xmlFile.name()};
            if (!object->addXmlFile(std::move(xmlFile))) {
                this->mLastError = "Duplicated manifest.xmlfile entry " + description +
                                   "; entries cannot have duplicated name and version";
                return false;
            }
        }

        return true;
    }
};

const HalManifestConverter halManifestConverter{};

const XmlTextConverter<Version> avbVersionConverter{"vbmeta-version"};
struct AvbConverter : public XmlNodeConverter<Version> {
    std::string elementName() const override { return "avb"; }
    void mutateNode(const Version &m, NodeType *root, DocType *d) const override {
        appendChild(root, avbVersionConverter(m, d));
    }
    bool buildObject(Version *object, NodeType *root) const override {
        return parseChild(root, avbVersionConverter, object);
    }
};
const AvbConverter avbConverter{};

struct MatrixXmlFileConverter : public XmlNodeConverter<MatrixXmlFile> {
    std::string elementName() const override { return "xmlfile"; }
    void mutateNode(const MatrixXmlFile& f, NodeType* root, DocType* d) const override {
        appendTextElement(root, "name", f.name(), d);
        appendAttr(root, "format", f.format());
        appendAttr(root, "optional", f.optional());
        appendChild(root, versionRangeConverter(f.versionRange(), d));
        if (!f.overriddenPath().empty()) {
            appendTextElement(root, "path", f.overriddenPath(), d);
        }
    }
    bool buildObject(MatrixXmlFile* object, NodeType* root) const override {
        if (!parseTextElement(root, "name", &object->mName) ||
            !parseAttr(root, "format", &object->mFormat) ||
            !parseOptionalAttr(root, "optional", false, &object->mOptional) ||
            !parseChild(root, versionRangeConverter, &object->mVersionRange) ||
            !parseOptionalTextElement(root, "path", {}, &object->mOverriddenPath)) {
            return false;
        }
        return true;
    }
};
const MatrixXmlFileConverter matrixXmlFileConverter{};

struct CompatibilityMatrixConverter : public XmlNodeConverter<CompatibilityMatrix> {
    std::string elementName() const override { return "compatibility-matrix"; }
    void mutateNode(const CompatibilityMatrix &m, NodeType *root, DocType *d) const override {
        mutateNode(m, root, d, SerializeFlag::EVERYTHING);
    }
    void mutateNode(const CompatibilityMatrix& m, NodeType* root, DocType* d,
                    SerializeFlags flags) const override {
        appendAttr(root, "version", m.getMinimumMetaVersion());
        appendAttr(root, "type", m.mType);
        if (m.mLevel != Level::UNSPECIFIED) {
            this->appendAttr(root, "level", m.mLevel);
        }

        if (!(flags & SerializeFlag::NO_HALS)) {
            appendChildren(root, matrixHalConverter, iterateValues(m.mHals), d);
        }
        if (m.mType == SchemaType::FRAMEWORK) {
            if (!(flags & SerializeFlag::NO_KERNEL)) {
                appendChildren(root, matrixKernelConverter, m.framework.mKernels, d);
            }
            if (!(flags & SerializeFlag::NO_SEPOLICY)) {
                appendChild(root, sepolicyConverter(m.framework.mSepolicy, d));
            }
            if (!(flags & SerializeFlag::NO_AVB)) {
                appendChild(root, avbConverter(m.framework.mAvbMetaVersion, d));
            }
        } else if (m.mType == SchemaType::DEVICE) {
            if (!(flags & SerializeFlag::NO_VNDK)) {
                appendChild(root, vndkConverter(m.device.mVndk, d));
            }
        }

        if (!(flags & SerializeFlag::NO_XMLFILES)) {
            appendChildren(root, matrixXmlFileConverter, m.getXmlFiles(), d);
        }
    }
    bool buildObject(CompatibilityMatrix *object, NodeType *root) const override {
        Version version;
        std::vector<MatrixHal> hals;
        if (!parseAttr(root, "version", &version) || !parseAttr(root, "type", &object->mType) ||
            !parseOptionalAttr(root, "level", Level::UNSPECIFIED, &object->mLevel) ||
            !parseChildren(root, matrixHalConverter, &hals)) {
            return false;
        }

        if (object->mType == SchemaType::FRAMEWORK) {
            // <avb> and <sepolicy> can be missing because it can be determined at build time, not
            // hard-coded in the XML file.
            if (!parseChildren(root, matrixKernelConverter, &object->framework.mKernels) ||
                !parseOptionalChild(root, sepolicyConverter, {}, &object->framework.mSepolicy) ||
                !parseOptionalChild(root, avbConverter, {}, &object->framework.mAvbMetaVersion)) {
                return false;
            }

            std::set<Version> seenKernelVersions;
            for (const auto& kernel : object->framework.mKernels) {
                Version minLts(kernel.minLts().version, kernel.minLts().majorRev);
                if (seenKernelVersions.find(minLts) != seenKernelVersions.end()) {
                    continue;
                }
                if (!kernel.conditions().empty()) {
                    this->mLastError = "First <kernel> for version " + to_string(minLts) +
                                       " must have empty <conditions> for backwards compatibility.";
                    return false;
                }
                seenKernelVersions.insert(minLts);
            }

        } else if (object->mType == SchemaType::DEVICE) {
            // <vndk> can be missing because it can be determined at build time, not hard-coded
            // in the XML file.
            if (!parseOptionalChild(root, vndkConverter, {}, &object->device.mVndk)) {
                return false;
            }
        }

        if (!kMetaVersion.minorAtLeast(version)) {
            this->mLastError = "Unrecognized compatibility-matrix.version " + to_string(version) +
                               " (libvintf@" + to_string(kMetaVersion) + ")";
            return false;
        }
        for (auto &&hal : hals) {
            if (!object->add(std::move(hal))) {
                this->mLastError = "Duplicated compatibility-matrix.hal entry";
                return false;
            }
        }

        std::vector<MatrixXmlFile> xmlFiles;
        if (!parseChildren(root, matrixXmlFileConverter, &xmlFiles)) {
            return false;
        }
        for (auto&& xmlFile : xmlFiles) {
            if (!xmlFile.optional()) {
                this->mLastError = "compatibility-matrix.xmlfile entry " + xmlFile.name() +
                                   " has to be optional for compatibility matrix version 1.0";
                return false;
            }
            std::string description{xmlFile.name()};
            if (!object->addXmlFile(std::move(xmlFile))) {
                this->mLastError = "Duplicated compatibility-matrix.xmlfile entry " + description;
                return false;
            }
        }

        return true;
    }
};

const CompatibilityMatrixConverter compatibilityMatrixConverter{};

// Publicly available as in parse_xml.h
const XmlConverter<HalManifest> &gHalManifestConverter = halManifestConverter;
const XmlConverter<CompatibilityMatrix> &gCompatibilityMatrixConverter
        = compatibilityMatrixConverter;

// For testing in LibVintfTest
const XmlConverter<Version> &gVersionConverter = versionConverter;
const XmlConverter<KernelConfigTypedValue> &gKernelConfigTypedValueConverter
        = kernelConfigTypedValueConverter;
const XmlConverter<MatrixHal> &gMatrixHalConverter = matrixHalConverter;
const XmlConverter<ManifestHal> &gManifestHalConverter = manifestHalConverter;

} // namespace vintf
} // namespace android