| /* |
| * 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. |
| |
| #include <tinyxml2.h> |
| |
| #include "parse_string.h" |
| #include "parse_xml.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_NO_ERROR) { |
| 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 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) const { |
| NodeType *root = createNode(this->elementName(), d); |
| this->mutateNode(o, root, d); |
| return root; |
| } |
| inline std::string serialize(const Object &o) const { |
| DocType *doc = createDocument(); |
| appendChild(doc, serialize(o, doc)); |
| 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) const { |
| return serialize(o); |
| } |
| 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 + "\" 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 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; |
| } |
| } |
| return true; |
| } |
| }; |
| |
| const MatrixHalConverter matrixHalConverter{}; |
| |
| 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); |
| appendChildren(root, kernelConfigConverter, kernel.mConfigs, d); |
| } |
| bool buildObject(MatrixKernel *object, NodeType *root) const override { |
| if (!parseAttr(root, "version", &object->mMinLts) || |
| !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); |
| if (!hal.transportArch.empty()) { |
| 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) || |
| !parseChild(root, transportArchConverter, &object->transportArch) || |
| !parseChildren(root, versionConverter, &object->versions) || |
| !parseChildren(root, halInterfaceConverter, &interfaces)) { |
| 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; |
| } |
| return true; |
| } |
| }; |
| |
| // 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 HalManifestConverter : public XmlNodeConverter<HalManifest> { |
| std::string elementName() const override { return "manifest"; } |
| void mutateNode(const HalManifest &m, NodeType *root, DocType *d) const override { |
| appendAttr(root, "version", HalManifest::kVersion); |
| appendAttr(root, "type", m.mType); |
| appendChildren(root, manifestHalConverter, m.getHals(), d); |
| if (m.mType == SchemaType::DEVICE) { |
| appendChild(root, halManifestSepolicyConverter(m.device.mSepolicyVersion, d)); |
| } else if (m.mType == SchemaType::FRAMEWORK) { |
| appendChildren(root, vndkConverter, m.framework.mVndks, d); |
| } |
| } |
| bool buildObject(HalManifest *object, NodeType *root) const override { |
| Version version; |
| std::vector<ManifestHal> hals; |
| if (!parseAttr(root, "version", &version) || |
| !parseAttr(root, "type", &object->mType) || |
| !parseChildren(root, manifestHalConverter, &hals)) { |
| return false; |
| } |
| if (version != HalManifest::kVersion) { |
| this->mLastError = "Unrecognized manifest.version"; |
| 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; |
| } |
| } |
| 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 CompatibilityMatrixConverter : public XmlNodeConverter<CompatibilityMatrix> { |
| std::string elementName() const override { return "compatibility-matrix"; } |
| void mutateNode(const CompatibilityMatrix &m, NodeType *root, DocType *d) const override { |
| appendAttr(root, "version", CompatibilityMatrix::kVersion); |
| appendAttr(root, "type", m.mType); |
| appendChildren(root, matrixHalConverter, iterateValues(m.mHals), d); |
| if (m.mType == SchemaType::FRAMEWORK) { |
| appendChildren(root, matrixKernelConverter, m.framework.mKernels, d); |
| appendChild(root, sepolicyConverter(m.framework.mSepolicy, d)); |
| appendChild(root, avbConverter(m.framework.mAvbMetaVersion, d)); |
| } else if (m.mType == SchemaType::DEVICE) { |
| appendChild(root, vndkConverter(m.device.mVndk, 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) || |
| !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; |
| } |
| } 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 (version != CompatibilityMatrix::kVersion) { |
| this->mLastError = "Unrecognized compatibility-matrix.version"; |
| return false; |
| } |
| for (auto &&hal : hals) { |
| if (!object->add(std::move(hal))) { |
| this->mLastError = "Duplicated compatibility-matrix.hal entry"; |
| 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 |