You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
958 lines
37 KiB
958 lines
37 KiB
/* |
|
* 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 "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_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 + "\" 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 { |
|
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); |
|
} |
|
|
|
appendChildren(root, manifestXmlFileConverter, m.getXmlFiles(), 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; |
|
} |
|
} |
|
|
|
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 { |
|
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)); |
|
} |
|
|
|
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) || |
|
!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 (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; |
|
} |
|
} |
|
|
|
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
|
|
|