| /* |
| * 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 "Regex.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, std::string* error) const = 0; |
| virtual std::string elementName() const = 0; |
| |
| // convenience methods for user |
| inline const std::string& lastError() const override { 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 override { |
| 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) { |
| bool ret = deserialize(object, root, &mLastError); |
| return ret; |
| } |
| inline bool deserialize(Object* o, const std::string& xml) override { |
| bool ret = (*this)(o, xml, &mLastError); |
| return ret; |
| } |
| inline bool deserialize(Object* object, NodeType* root, std::string* error) const { |
| if (nameOf(root) != this->elementName()) { |
| return false; |
| } |
| return this->buildObject(object, root, error); |
| } |
| inline bool operator()(Object* o, const std::string& xml, std::string* error) const override { |
| std::string errorBuffer; |
| if (error == nullptr) error = &errorBuffer; |
| |
| auto doc = createDocument(xml); |
| if (doc == nullptr) { |
| *error = "Not a valid XML"; |
| return false; |
| } |
| bool ret = deserialize(o, getRootChild(doc), error); |
| 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 override { |
| return serialize(o, flags); |
| } |
| inline bool operator()(Object* o, NodeType* node) { return deserialize(o, node); } |
| inline bool operator()(Object* o, const std::string& xml) override { |
| 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, |
| SerializeFlags flags = SerializeFlag::EVERYTHING) const { |
| for (const T &t : array) { |
| appendChild(parent, conv.serialize(t, d, flags)); |
| } |
| } |
| |
| // All parse* functions helps buildObject() to deserialize XML to the object. Returns |
| // true if deserialization is successful, false if any error, and "error" will be |
| // set to error message. |
| template <typename T> |
| inline bool parseOptionalAttr(NodeType* root, const std::string& attrName, T&& defaultValue, |
| T* attr, std::string* /* error */) 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, |
| std::string* error) const { |
| std::string attrText; |
| bool ret = getAttr(root, attrName, &attrText) && ::android::vintf::parse(attrText, attr); |
| if (!ret) { |
| *error = "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, |
| std::string* error) const { |
| bool ret = getAttr(root, attrName, attr); |
| if (!ret) { |
| *error = "Could not find attr with name \"" + attrName + "\" for element <" + |
| elementName() + ">"; |
| } |
| return ret; |
| } |
| |
| inline bool parseTextElement(NodeType* root, const std::string& elementName, std::string* s, |
| std::string* error) const { |
| NodeType *child = getChild(root, elementName); |
| if (child == nullptr) { |
| *error = "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, |
| std::string* /* error */) 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, std::string* /* error */) 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, |
| std::string* error) const { |
| NodeType *child = getChild(root, conv.elementName()); |
| if (child == nullptr) { |
| *error = "Could not find element with name <" + conv.elementName() + "> in element <" + |
| this->elementName() + ">"; |
| return false; |
| } |
| return conv.deserialize(t, child, error); |
| } |
| |
| template <typename T> |
| inline bool parseOptionalChild(NodeType* root, const XmlNodeConverter<T>& conv, |
| T&& defaultValue, T* t, std::string* error) const { |
| NodeType *child = getChild(root, conv.elementName()); |
| if (child == nullptr) { |
| *t = std::move(defaultValue); |
| return true; |
| } |
| return conv.deserialize(t, child, error); |
| } |
| |
| template <typename T> |
| inline bool parseChildren(NodeType* root, const XmlNodeConverter<T>& conv, std::vector<T>* v, |
| std::string* error) 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], error)) { |
| *error = "Could not parse element with name <" + conv.elementName() + |
| "> in element <" + this->elementName() + ">: " + *error; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| template <typename T> |
| inline bool parseChildren(NodeType* root, const XmlNodeConverter<T>& conv, std::set<T>* s, |
| std::string* error) const { |
| std::vector<T> vec; |
| if (!parseChildren(root, conv, &vec, error)) { |
| return false; |
| } |
| s->clear(); |
| s->insert(vec.begin(), vec.end()); |
| if (s->size() != vec.size()) { |
| *error = "Duplicated elements <" + conv.elementName() + "> in element <" + |
| this->elementName() + ">"; |
| s->clear(); |
| return false; |
| } |
| return true; |
| } |
| |
| inline bool parseText(NodeType* node, std::string* s, std::string* /* error */) const { |
| *s = getText(node); |
| return true; |
| } |
| |
| template <typename T> |
| inline bool parseText(NodeType* node, T* s, std::string* error) const { |
| std::string text = getText(node); |
| bool ret = ::android::vintf::parse(text, s); |
| if (!ret) { |
| *error = "Could not parse text \"" + text + "\" in element <" + elementName() + ">"; |
| } |
| return ret; |
| } |
| |
| private: |
| 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, std::string* error) const override { |
| return this->parseText(root, object, error); |
| } |
| virtual std::string elementName() const { return mElementName; }; |
| private: |
| std::string mElementName; |
| }; |
| |
| // ---------------------- 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 { |
| 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, std::string* error) const override { |
| if (!parseOptionalAttr(root, "arch", Arch::ARCH_EMPTY, &object->arch, error) || |
| !parseText(root, &object->transport, error)) { |
| return false; |
| } |
| if (!object->isValid()) { |
| *error = "transport == " + ::android::vintf::to_string(object->transport) + |
| " and arch == " + ::android::vintf::to_string(object->arch) + |
| " is not a valid combination."; |
| return false; |
| } |
| return true; |
| } |
| }; |
| |
| 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, |
| std::string* error) const override { |
| std::string stringValue; |
| if (!parseAttr(root, "type", &object->mType, error) || |
| !parseText(root, &stringValue, error)) { |
| return false; |
| } |
| if (!::android::vintf::parseKernelConfigValue(stringValue, object)) { |
| *error = "Could not parse kernel config value \"" + stringValue + "\""; |
| return false; |
| } |
| return true; |
| } |
| }; |
| |
| 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{}; |
| |
| 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.mInstances, d); |
| appendTextElements(root, "regex-instance", intf.mRegexes, d); |
| } |
| bool buildObject(HalInterface* intf, NodeType* root, std::string* error) const override { |
| std::vector<std::string> instances; |
| std::vector<std::string> regexes; |
| if (!parseTextElement(root, "name", &intf->mName, error) || |
| !parseTextElements(root, "instance", &instances, error) || |
| !parseTextElements(root, "regex-instance", ®exes, error)) { |
| return false; |
| } |
| bool success = true; |
| for (const auto& e : instances) { |
| if (!intf->insertInstance(e, false /* isRegex */)) { |
| if (!error->empty()) *error += "\n"; |
| *error += "Duplicated instance '" + e + "' in " + intf->name(); |
| success = false; |
| } |
| } |
| for (const auto& e : regexes) { |
| details::Regex regex; |
| if (!regex.compile(e)) { |
| if (!error->empty()) *error += "\n"; |
| *error += "Invalid regular expression '" + e + "' in " + intf->name(); |
| success = false; |
| } |
| if (!intf->insertInstance(e, true /* isRegex */)) { |
| if (!error->empty()) *error += "\n"; |
| *error += "Duplicated regex-instance '" + e + "' in " + intf->name(); |
| success = false; |
| } |
| } |
| return success; |
| } |
| }; |
| |
| 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, std::string* error) const override { |
| std::vector<HalInterface> interfaces; |
| if (!parseOptionalAttr(root, "format", HalFormat::HIDL, &object->format, error) || |
| !parseOptionalAttr(root, "optional", false /* defaultValue */, &object->optional, |
| error) || |
| !parseTextElement(root, "name", &object->name, error) || |
| !parseChildren(root, versionRangeConverter, &object->versionRanges, error) || |
| !parseChildren(root, halInterfaceConverter, &interfaces, error)) { |
| 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) { |
| *error = "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. |
| #ifndef LIBVINTF_TARGET |
| if (!checkAdditionalRestrictionsOnHal(*object, error)) { |
| return false; |
| } |
| #endif |
| return true; |
| } |
| |
| #ifndef LIBVINTF_TARGET |
| private: |
| bool checkAdditionalRestrictionsOnHal(const MatrixHal& hal, std::string* error) const { |
| if (hal.getName() == "netutils-wrapper") { |
| if (hal.versionRanges.size() != 1) { |
| *error = |
| "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()) { |
| *error = |
| "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) { |
| *error = |
| "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 |
| }; |
| |
| 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, |
| std::string* error) const override { |
| return parseChildren(root, kernelConfigConverter, object, error); |
| } |
| }; |
| |
| 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, std::string* error) const override { |
| if (!parseAttr(root, "version", &object->mMinLts, error) || |
| !parseOptionalChild(root, matrixKernelConditionsConverter, {}, &object->mConditions, |
| error) || |
| !parseChildren(root, kernelConfigConverter, &object->mConfigs, error)) { |
| return false; |
| } |
| return true; |
| } |
| }; |
| |
| MatrixKernelConverter matrixKernelConverter{}; |
| |
| XmlTextConverter<FqInstance> fqInstanceConverter{"fqname"}; |
| |
| struct ManifestHalConverter : public XmlNodeConverter<ManifestHal> { |
| std::string elementName() const override { return "hal"; } |
| void mutateNode(const ManifestHal& m, NodeType* root, DocType* d) const override { |
| mutateNode(m, root, d, SerializeFlag::EVERYTHING); |
| } |
| void mutateNode(const ManifestHal& hal, NodeType* root, DocType* d, |
| SerializeFlags flags) 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); |
| if (hal.isOverride()) { |
| appendAttr(root, "override", hal.isOverride()); |
| } |
| |
| if (!(flags & SerializeFlag::NO_FQNAME)) { |
| std::set<FqInstance> fqInstances; |
| hal.forEachInstance([&fqInstances](const auto& manifestInstance) { |
| fqInstances.emplace(manifestInstance.getFqInstanceNoPackage()); |
| return true; |
| }); |
| appendChildren(root, fqInstanceConverter, fqInstances, d); |
| } |
| } |
| bool buildObject(ManifestHal* object, NodeType* root, std::string* error) const override { |
| std::vector<HalInterface> interfaces; |
| if (!parseOptionalAttr(root, "format", HalFormat::HIDL, &object->format, error) || |
| !parseOptionalAttr(root, "override", false, &object->mIsOverride, error) || |
| !parseTextElement(root, "name", &object->name, error) || |
| !parseOptionalChild(root, transportArchConverter, {}, &object->transportArch, error) || |
| !parseChildren(root, versionConverter, &object->versions, error) || |
| !parseChildren(root, halInterfaceConverter, &interfaces, error)) { |
| return false; |
| } |
| |
| switch (object->format) { |
| case HalFormat::HIDL: { |
| if (object->transportArch.empty()) { |
| *error = "HIDL HAL '" + object->name + "' should have <transport> defined."; |
| return false; |
| } |
| } break; |
| case HalFormat::NATIVE: { |
| if (!object->transportArch.empty()) { |
| *error = |
| "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) { |
| *error = "Duplicated interface entry \"" + res.first->first + |
| "\"; if additional instances are needed, add them to the " |
| "existing <interface> node."; |
| return false; |
| } |
| } |
| if (!object->isValid()) { |
| *error = "'" + object->name + "' is not a valid Manifest HAL."; |
| return false; |
| } |
| // Do not check for target-side libvintf to avoid restricting upgrade accidentally. |
| #ifndef LIBVINTF_TARGET |
| if (!checkAdditionalRestrictionsOnHal(*object, error)) { |
| return false; |
| } |
| #endif |
| |
| std::set<FqInstance> fqInstances; |
| if (!parseChildren(root, fqInstanceConverter, &fqInstances, error)) { |
| return false; |
| } |
| if (!object->insertInstances(fqInstances, error)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| #ifndef LIBVINTF_TARGET |
| private: |
| bool checkAdditionalRestrictionsOnHal(const ManifestHal& hal, std::string* error) const { |
| if (hal.getName() == "netutils-wrapper") { |
| for (const Version& v : hal.versions) { |
| if (v.minorVer != 0) { |
| *error = |
| "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. |
| ManifestHalConverter manifestHalConverter{}; |
| |
| XmlTextConverter<KernelSepolicyVersion> kernelSepolicyVersionConverter{"kernel-sepolicy-version"}; |
| 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, std::string* error) const override { |
| if (!parseChild(root, kernelSepolicyVersionConverter, &object->mKernelSepolicyVersion, |
| error) || |
| !parseChildren(root, sepolicyVersionConverter, &object->mSepolicyVersionRanges, |
| error)) { |
| return false; |
| } |
| return true; |
| } |
| }; |
| SepolicyConverter sepolicyConverter{}; |
| |
| [[deprecated]] XmlTextConverter<VndkVersionRange> vndkVersionRangeConverter{"version"}; |
| |
| XmlTextConverter<std::string> vndkVersionConverter{"version"}; |
| XmlTextConverter<std::string> vndkLibraryConverter{"library"}; |
| |
| struct [[deprecated]] 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, std::string* error) const override { |
| if (!parseChild(root, vndkVersionRangeConverter, &object->mVersionRange, error) || |
| !parseChildren(root, vndkLibraryConverter, &object->mLibraries, error)) { |
| return false; |
| } |
| return true; |
| } |
| }; |
| |
| [[deprecated]] VndkConverter vndkConverter{}; |
| |
| struct VendorNdkConverter : public XmlNodeConverter<VendorNdk> { |
| std::string elementName() const override { return "vendor-ndk"; } |
| void mutateNode(const VendorNdk& object, NodeType* root, DocType* d) const override { |
| appendChild(root, vndkVersionConverter(object.mVersion, d)); |
| appendChildren(root, vndkLibraryConverter, object.mLibraries, d); |
| } |
| bool buildObject(VendorNdk* object, NodeType* root, std::string* error) const override { |
| if (!parseChild(root, vndkVersionConverter, &object->mVersion, error) || |
| !parseChildren(root, vndkLibraryConverter, &object->mLibraries, error)) { |
| return false; |
| } |
| return true; |
| } |
| }; |
| |
| VendorNdkConverter vendorNdkConverter{}; |
| |
| XmlTextConverter<std::string> systemSdkVersionConverter{"version"}; |
| |
| struct SystemSdkConverter : public XmlNodeConverter<SystemSdk> { |
| std::string elementName() const override { return "system-sdk"; } |
| void mutateNode(const SystemSdk& object, NodeType* root, DocType* d) const override { |
| appendChildren(root, systemSdkVersionConverter, object.versions(), d); |
| } |
| bool buildObject(SystemSdk* object, NodeType* root, std::string* error) const override { |
| return parseChildren(root, systemSdkVersionConverter, &object->mVersions, error); |
| } |
| }; |
| |
| SystemSdkConverter systemSdkConverter{}; |
| |
| 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, std::string* error) const override { |
| return parseChild(root, versionConverter, object, error); |
| } |
| }; |
| 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, std::string* error) const override { |
| if (!parseTextElement(root, "name", &object->mName, error) || |
| !parseChild(root, versionConverter, &object->mVersion, error) || |
| !parseOptionalTextElement(root, "path", {}, &object->mOverriddenPath, error)) { |
| return false; |
| } |
| return true; |
| } |
| }; |
| 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 (!(flags & SerializeFlag::NO_HALS)) { |
| appendChildren(root, manifestHalConverter, m.getHals(), d, flags); |
| } |
| if (m.mType == SchemaType::DEVICE) { |
| if (!(flags & SerializeFlag::NO_SEPOLICY)) { |
| appendChild(root, halManifestSepolicyConverter(m.device.mSepolicyVersion, d)); |
| } |
| if (m.mLevel != Level::UNSPECIFIED) { |
| this->appendAttr(root, "target-level", m.mLevel); |
| } |
| } else if (m.mType == SchemaType::FRAMEWORK) { |
| if (!(flags & SerializeFlag::NO_VNDK)) { |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| appendChildren(root, vndkConverter, m.framework.mVndks, d); |
| #pragma clang diagnostic pop |
| |
| appendChildren(root, vendorNdkConverter, m.framework.mVendorNdks, d); |
| } |
| if (!(flags & SerializeFlag::NO_SSDK)) { |
| if (!m.framework.mSystemSdk.empty()) { |
| appendChild(root, systemSdkConverter(m.framework.mSystemSdk, d)); |
| } |
| } |
| } |
| |
| if (!(flags & SerializeFlag::NO_XMLFILES)) { |
| appendChildren(root, manifestXmlFileConverter, m.getXmlFiles(), d); |
| } |
| } |
| bool buildObject(HalManifest* object, NodeType* root, std::string* error) const override { |
| std::vector<ManifestHal> hals; |
| if (!parseAttr(root, "version", &object->mMetaVersion, error) || |
| !parseAttr(root, "type", &object->mType, error) || |
| !parseChildren(root, manifestHalConverter, &hals, error)) { |
| return false; |
| } |
| if (!kMetaVersion.minorAtLeast(object->mMetaVersion)) { |
| *error = "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, error)) { |
| return false; |
| } |
| |
| if (!parseOptionalAttr(root, "target-level", Level::UNSPECIFIED, &object->mLevel, |
| error)) { |
| return false; |
| } |
| } else if (object->mType == SchemaType::FRAMEWORK) { |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| if (!parseChildren(root, vndkConverter, &object->framework.mVndks, error)) { |
| return false; |
| } |
| for (const auto &vndk : object->framework.mVndks) { |
| if (!vndk.mVersionRange.isSingleVersion()) { |
| *error = "vndk.version " + to_string(vndk.mVersionRange) + |
| " cannot be a range for manifests"; |
| return false; |
| } |
| } |
| #pragma clang diagnostic pop |
| |
| if (!parseChildren(root, vendorNdkConverter, &object->framework.mVendorNdks, error)) { |
| return false; |
| } |
| |
| std::set<std::string> vendorNdkVersions; |
| for (const auto& vendorNdk : object->framework.mVendorNdks) { |
| if (vendorNdkVersions.find(vendorNdk.version()) != vendorNdkVersions.end()) { |
| *error = "Duplicated manifest.vendor-ndk.version " + vendorNdk.version(); |
| return false; |
| } |
| vendorNdkVersions.insert(vendorNdk.version()); |
| } |
| |
| if (!parseOptionalChild(root, systemSdkConverter, {}, &object->framework.mSystemSdk, |
| error)) { |
| return false; |
| } |
| } |
| for (auto &&hal : hals) { |
| std::string description{hal.name}; |
| if (!object->add(std::move(hal))) { |
| *error = "Duplicated manifest.hal entry " + description; |
| return false; |
| } |
| } |
| |
| std::vector<ManifestXmlFile> xmlFiles; |
| if (!parseChildren(root, manifestXmlFileConverter, &xmlFiles, error)) { |
| return false; |
| } |
| for (auto&& xmlFile : xmlFiles) { |
| std::string description{xmlFile.name()}; |
| if (!object->addXmlFile(std::move(xmlFile))) { |
| *error = "Duplicated manifest.xmlfile entry " + description + |
| "; entries cannot have duplicated name and version"; |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| }; |
| |
| HalManifestConverter halManifestConverter{}; |
| |
| 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, std::string* error) const override { |
| return parseChild(root, avbVersionConverter, object, error); |
| } |
| }; |
| 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, std::string* error) const override { |
| if (!parseTextElement(root, "name", &object->mName, error) || |
| !parseAttr(root, "format", &object->mFormat, error) || |
| !parseOptionalAttr(root, "optional", false, &object->mOptional, error) || |
| !parseChild(root, versionRangeConverter, &object->mVersionRange, error) || |
| !parseOptionalTextElement(root, "path", {}, &object->mOverriddenPath, error)) { |
| return false; |
| } |
| return true; |
| } |
| }; |
| 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 (!(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)) { |
| if (!(m.framework.mSepolicy == Sepolicy{})) { |
| appendChild(root, sepolicyConverter(m.framework.mSepolicy, d)); |
| } |
| } |
| if (!(flags & SerializeFlag::NO_AVB)) { |
| if (!(m.framework.mAvbMetaVersion == Version{})) { |
| appendChild(root, avbConverter(m.framework.mAvbMetaVersion, d)); |
| } |
| } |
| if (m.mLevel != Level::UNSPECIFIED) { |
| this->appendAttr(root, "level", m.mLevel); |
| } |
| } else if (m.mType == SchemaType::DEVICE) { |
| if (!(flags & SerializeFlag::NO_VNDK)) { |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| if (!(m.device.mVndk == Vndk{})) { |
| appendChild(root, vndkConverter(m.device.mVndk, d)); |
| } |
| #pragma clang diagnostic pop |
| |
| if (!(m.device.mVendorNdk == VendorNdk{})) { |
| appendChild(root, vendorNdkConverter(m.device.mVendorNdk, d)); |
| } |
| } |
| |
| if (!(flags & SerializeFlag::NO_SSDK)) { |
| if (!m.device.mSystemSdk.empty()) { |
| appendChild(root, systemSdkConverter(m.device.mSystemSdk, d)); |
| } |
| } |
| } |
| |
| if (!(flags & SerializeFlag::NO_XMLFILES)) { |
| appendChildren(root, matrixXmlFileConverter, m.getXmlFiles(), d); |
| } |
| } |
| bool buildObject(CompatibilityMatrix* object, NodeType* root, |
| std::string* error) const override { |
| Version version; |
| std::vector<MatrixHal> hals; |
| if (!parseAttr(root, "version", &version, error) || |
| !parseAttr(root, "type", &object->mType, error) || |
| !parseChildren(root, matrixHalConverter, &hals, error)) { |
| 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, error) || |
| !parseOptionalChild(root, sepolicyConverter, {}, &object->framework.mSepolicy, |
| error) || |
| !parseOptionalChild(root, avbConverter, {}, &object->framework.mAvbMetaVersion, |
| error)) { |
| 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()) { |
| *error = "First <kernel> for version " + to_string(minLts) + |
| " must have empty <conditions> for backwards compatibility."; |
| return false; |
| } |
| seenKernelVersions.insert(minLts); |
| } |
| |
| if (!parseOptionalAttr(root, "level", Level::UNSPECIFIED, &object->mLevel, error)) { |
| 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. |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| if (!parseOptionalChild(root, vndkConverter, {}, &object->device.mVndk, error)) { |
| return false; |
| } |
| #pragma clang diagnostic pop |
| |
| if (!parseOptionalChild(root, vendorNdkConverter, {}, &object->device.mVendorNdk, |
| error)) { |
| return false; |
| } |
| |
| if (!parseOptionalChild(root, systemSdkConverter, {}, &object->device.mSystemSdk, |
| error)) { |
| return false; |
| } |
| } |
| |
| if (!kMetaVersion.minorAtLeast(version)) { |
| *error = "Unrecognized compatibility-matrix.version " + to_string(version) + |
| " (libvintf@" + to_string(kMetaVersion) + ")"; |
| return false; |
| } |
| for (auto &&hal : hals) { |
| if (!object->add(std::move(hal))) { |
| *error = "Duplicated compatibility-matrix.hal entry"; |
| return false; |
| } |
| } |
| |
| std::vector<MatrixXmlFile> xmlFiles; |
| if (!parseChildren(root, matrixXmlFileConverter, &xmlFiles, error)) { |
| return false; |
| } |
| for (auto&& xmlFile : xmlFiles) { |
| if (!xmlFile.optional()) { |
| *error = "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))) { |
| *error = "Duplicated compatibility-matrix.xmlfile entry " + description; |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| }; |
| |
| CompatibilityMatrixConverter compatibilityMatrixConverter{}; |
| |
| // Publicly available as in parse_xml.h |
| XmlConverter<HalManifest>& gHalManifestConverter = halManifestConverter; |
| XmlConverter<CompatibilityMatrix>& gCompatibilityMatrixConverter = compatibilityMatrixConverter; |
| |
| // For testing in LibVintfTest |
| XmlConverter<Version>& gVersionConverter = versionConverter; |
| XmlConverter<KernelConfigTypedValue>& gKernelConfigTypedValueConverter = |
| kernelConfigTypedValueConverter; |
| XmlConverter<MatrixHal>& gMatrixHalConverter = matrixHalConverter; |
| XmlConverter<ManifestHal>& gManifestHalConverter = manifestHalConverter; |
| |
| } // namespace vintf |
| } // namespace android |