/*
* 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