parse_xml.cpp: Deserilization does not touch lastError()

The global mLastError fields for each converters can
potentially cause a segfault when a program with multiple
threads tries to deserialize a bad file at the same time.

- deserialization APIs that touches the mLastError field are
  marked as non-const APIs
- const variants are provided (error is provided as an output
  parameter)
- Functionalities are equivalent (tests ensure this).

Test: libvintf_test
Test: vintf_object_test
Fixes: 71874788

Change-Id: I416de909b32809a4ac377d9da998c48d7d409457
diff --git a/parse_xml.cpp b/parse_xml.cpp
index 7abbc55..739e344 100644
--- a/parse_xml.cpp
+++ b/parse_xml.cpp
@@ -143,50 +143,59 @@
     virtual void mutateNode(const Object& o, NodeType* n, DocType* d, SerializeFlags) const {
         mutateNode(o, n, d);
     }
-    virtual bool buildObject(Object *o, NodeType *n) const = 0;
+    virtual 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 { return mLastError; }
+    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 {
+    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) const {
+    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);
+        return this->buildObject(object, root, error);
     }
-    inline bool deserialize(Object *o, const std::string &xml) const {
-        DocType *doc = createDocument(xml);
+    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) {
-            this->mLastError = "Not a valid XML";
+            *error = "Not a valid XML";
             return false;
         }
-        bool ret = deserialize(o, getRootChild(doc));
+        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 {
+    inline std::string operator()(const Object& o, SerializeFlags flags) const override {
         return serialize(o, flags);
     }
-    inline bool operator()(Object *o, NodeType *node) const {
-        return deserialize(o, node);
-    }
-    inline bool operator()(Object *o, const std::string &xml) const {
+    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);
     }
 
@@ -230,11 +239,11 @@
     }
 
     // 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
+    // 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) const {
+    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);
@@ -245,31 +254,33 @@
     }
 
     template <typename T>
-    inline bool parseAttr(NodeType *root, const std::string &attrName, T *attr) const {
+    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) {
-            mLastError = "Could not find/parse attr with name \"" + attrName + "\" and value \"" +
-                         attrText + "\" for element <" + elementName() + ">";
+            *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) const {
+    inline bool parseAttr(NodeType* root, const std::string& attrName, std::string* attr,
+                          std::string* error) const {
         bool ret = getAttr(root, attrName, attr);
         if (!ret) {
-            mLastError = "Could not find attr with name \"" + attrName + "\" for element <"
-                    + elementName() + ">";
+            *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) const {
+    inline bool parseTextElement(NodeType* root, const std::string& elementName, std::string* s,
+                                 std::string* error) const {
         NodeType *child = getChild(root, elementName);
         if (child == nullptr) {
-            mLastError = "Could not find element with name <" + elementName + "> in element <"
-                    + this->elementName() + ">";
+            *error = "Could not find element with name <" + elementName + "> in element <" +
+                     this->elementName() + ">";
             return false;
         }
         *s = getText(child);
@@ -277,14 +288,15 @@
     }
 
     inline bool parseOptionalTextElement(NodeType* root, const std::string& elementName,
-                                         std::string&& defaultValue, std::string* s) const {
+                                         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) const {
+    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) {
@@ -294,43 +306,37 @@
     }
 
     template <typename T>
-    inline bool parseChild(NodeType *root, const XmlNodeConverter<T> &conv, T *t) const {
+    inline bool parseChild(NodeType* root, const XmlNodeConverter<T>& conv, T* t,
+                           std::string* error) const {
         NodeType *child = getChild(root, conv.elementName());
         if (child == nullptr) {
-            mLastError = "Could not find element with name <" + conv.elementName() + "> in element <"
-                    + this->elementName() + ">";
+            *error = "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;
+        return conv.deserialize(t, child, error);
     }
 
     template <typename T>
-    inline bool parseOptionalChild(NodeType *root, const XmlNodeConverter<T> &conv,
-            T &&defaultValue, T *t) const {
+    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;
         }
-        bool success = conv.deserialize(t, child);
-        if (!success) {
-            mLastError = conv.lastError();
-        }
-        return success;
+        return conv.deserialize(t, child, error);
     }
 
     template <typename T>
-    inline bool parseChildren(NodeType *root, const XmlNodeConverter<T> &conv, std::vector<T> *v) const {
+    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])) {
-                mLastError = "Could not parse element with name <" + conv.elementName()
-                        + "> in element <" + this->elementName() + ">: " + conv.lastError();
+            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;
             }
         }
@@ -338,37 +344,39 @@
     }
 
     template <typename T>
-    inline bool parseChildren(NodeType *root, const XmlNodeConverter<T> &conv, std::set<T> *s) const {
+    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)) {
+        if (!parseChildren(root, conv, &vec, error)) {
             return false;
         }
         s->clear();
         s->insert(vec.begin(), vec.end());
         if (s->size() != vec.size()) {
-            mLastError = "Duplicated elements <" + conv.elementName() + "> in element <"
-                    + this->elementName() + ">";
+            *error = "Duplicated elements <" + conv.elementName() + "> in element <" +
+                     this->elementName() + ">";
             s->clear();
             return false;
         }
         return true;
     }
 
-    inline bool parseText(NodeType *node, std::string *s) const {
+    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) const {
+    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) {
-            mLastError = "Could not parse text \"" + text + "\" in element <" + elementName() + ">";
+            *error = "Could not parse text \"" + text + "\" in element <" + elementName() + ">";
         }
         return ret;
     }
-protected:
+
+   private:
     mutable std::string mLastError;
 };
 
@@ -380,8 +388,8 @@
     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 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:
@@ -390,11 +398,11 @@
 
 // ---------------------- XmlNodeConverter definitions end
 
-const XmlTextConverter<Version> versionConverter{"version"};
+XmlTextConverter<Version> versionConverter{"version"};
 
-const XmlTextConverter<VersionRange> versionRangeConverter{"version"};
+XmlTextConverter<VersionRange> versionRangeConverter{"version"};
 
-const XmlTextConverter<KernelConfigKey> kernelConfigKeyConverter{"key"};
+XmlTextConverter<KernelConfigKey> kernelConfigKeyConverter{"key"};
 
 struct TransportArchConverter : public XmlNodeConverter<TransportArch> {
     std::string elementName() const override { return "transport"; }
@@ -404,22 +412,22 @@
         }
         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)) {
+    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()) {
-            this->mLastError = "transport == " + ::android::vintf::to_string(object->transport) +
-                    " and arch == " + ::android::vintf::to_string(object->arch) +
-                    " is not a valid combination.";
+            *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;
     }
 };
 
-const TransportArchConverter transportArchConverter{};
+TransportArchConverter transportArchConverter{};
 
 struct KernelConfigTypedValueConverter : public XmlNodeConverter<KernelConfigTypedValue> {
     std::string elementName() const override { return "value"; }
@@ -427,21 +435,22 @@
         appendAttr(root, "type", object.mType);
         appendText(root, ::android::vintf::to_string(object), d);
     }
-    bool buildObject(KernelConfigTypedValue *object, NodeType *root) const override {
+    bool buildObject(KernelConfigTypedValue* object, NodeType* root,
+                     std::string* error) const override {
         std::string stringValue;
-        if (!parseAttr(root, "type", &object->mType) ||
-            !parseText(root, &stringValue)) {
+        if (!parseAttr(root, "type", &object->mType, error) ||
+            !parseText(root, &stringValue, error)) {
             return false;
         }
         if (!::android::vintf::parseKernelConfigValue(stringValue, object)) {
-            this->mLastError = "Could not parse kernel config value \"" + stringValue + "\"";
+            *error = "Could not parse kernel config value \"" + stringValue + "\"";
             return false;
         }
         return true;
     }
 };
 
-const KernelConfigTypedValueConverter kernelConfigTypedValueConverter{};
+KernelConfigTypedValueConverter kernelConfigTypedValueConverter{};
 
 struct KernelConfigConverter : public XmlNodeConverter<KernelConfig> {
     std::string elementName() const override { return "config"; }
@@ -449,16 +458,16 @@
         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)) {
+    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;
     }
 };
 
-const KernelConfigConverter kernelConfigConverter{};
+KernelConfigConverter kernelConfigConverter{};
 
 struct HalInterfaceConverter : public XmlNodeConverter<HalInterface> {
     std::string elementName() const override { return "interface"; }
@@ -466,23 +475,23 @@
         appendTextElement(root, "name", intf.name, d);
         appendTextElements(root, "instance", intf.instances, d);
     }
-    bool buildObject(HalInterface *intf, NodeType *root) const override {
+    bool buildObject(HalInterface* intf, NodeType* root, std::string* error) const override {
         std::vector<std::string> instances;
-        if (!parseTextElement(root, "name", &intf->name) ||
-            !parseTextElements(root, "instance", &instances)) {
+        if (!parseTextElement(root, "name", &intf->name, error) ||
+            !parseTextElements(root, "instance", &instances, error)) {
             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;
+            *error = "Duplicated instances in " + intf->name;
             return false;
         }
         return true;
     }
 };
 
-const HalInterfaceConverter halInterfaceConverter{};
+HalInterfaceConverter halInterfaceConverter{};
 
 struct MatrixHalConverter : public XmlNodeConverter<MatrixHal> {
     std::string elementName() const override { return "hal"; }
@@ -493,28 +502,29 @@
         appendChildren(root, versionRangeConverter, hal.versionRanges, d);
         appendChildren(root, halInterfaceConverter, iterateValues(hal.interfaces), d);
     }
-    bool buildObject(MatrixHal *object, NodeType *root) const override {
+    bool buildObject(MatrixHal* object, NodeType* root, std::string* error) 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)) {
+        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) {
-                this->mLastError = "Duplicated interface entry \"" + res.first->first +
-                                   "\"; if additional instances are needed, add them to the "
-                                   "existing <interface> node.";
+                *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)) {
+        if (!checkAdditionalRestrictionsOnHal(*object, error)) {
             return false;
         }
 #endif
@@ -523,24 +533,24 @@
 
 #ifndef LIBVINTF_TARGET
    private:
-    bool checkAdditionalRestrictionsOnHal(const MatrixHal& hal) const {
+    bool checkAdditionalRestrictionsOnHal(const MatrixHal& hal, std::string* error) const {
         if (hal.getName() == "netutils-wrapper") {
             if (hal.versionRanges.size() != 1) {
-                this->mLastError =
+                *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()) {
-                this->mLastError =
+                *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) {
-                this->mLastError =
+                *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}) + "'?";
@@ -552,7 +562,7 @@
 #endif
 };
 
-const MatrixHalConverter matrixHalConverter{};
+MatrixHalConverter matrixHalConverter{};
 
 struct MatrixKernelConditionsConverter : public XmlNodeConverter<std::vector<KernelConfig>> {
     std::string elementName() const override { return "conditions"; }
@@ -560,12 +570,13 @@
                     DocType* d) const override {
         appendChildren(root, kernelConfigConverter, conds, d);
     }
-    bool buildObject(std::vector<KernelConfig>* object, NodeType* root) const override {
-        return parseChildren(root, kernelConfigConverter, object);
+    bool buildObject(std::vector<KernelConfig>* object, NodeType* root,
+                     std::string* error) const override {
+        return parseChildren(root, kernelConfigConverter, object, error);
     }
 };
 
-const MatrixKernelConditionsConverter matrixKernelConditionsConverter{};
+MatrixKernelConditionsConverter matrixKernelConditionsConverter{};
 
 struct MatrixKernelConverter : public XmlNodeConverter<MatrixKernel> {
     std::string elementName() const override { return "kernel"; }
@@ -576,17 +587,18 @@
         }
         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)) {
+    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;
     }
 };
 
-const MatrixKernelConverter matrixKernelConverter{};
+MatrixKernelConverter matrixKernelConverter{};
 
 struct ManifestHalConverter : public XmlNodeConverter<ManifestHal> {
     std::string elementName() const override { return "hal"; }
@@ -600,28 +612,27 @@
             appendAttr(root, "override", hal.isOverride);
         }
     }
-    bool buildObject(ManifestHal *object, NodeType *root) const override {
+    bool buildObject(ManifestHal* object, NodeType* root, std::string* error) const override {
         std::vector<HalInterface> interfaces;
-        if (!parseOptionalAttr(root, "format", HalFormat::HIDL, &object->format) ||
-            !parseOptionalAttr(root, "override", false, &object->isOverride) ||
-            !parseTextElement(root, "name", &object->name) ||
-            !parseOptionalChild(root, transportArchConverter, {}, &object->transportArch) ||
-            !parseChildren(root, versionConverter, &object->versions) ||
-            !parseChildren(root, halInterfaceConverter, &interfaces)) {
+        if (!parseOptionalAttr(root, "format", HalFormat::HIDL, &object->format, error) ||
+            !parseOptionalAttr(root, "override", false, &object->isOverride, 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()) {
-                    this->mLastError =
-                        "HIDL HAL '" + object->name + "' should have <transport> defined.";
+                    *error = "HIDL HAL '" + object->name + "' should have <transport> defined.";
                     return false;
                 }
             } break;
             case HalFormat::NATIVE: {
                 if (!object->transportArch.empty()) {
-                    this->mLastError =
+                    *error =
                         "Native HAL '" + object->name + "' should not have <transport> defined.";
                     return false;
                 }
@@ -639,19 +650,19 @@
             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.";
+                *error = "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.";
+            *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)) {
+        if (!checkAdditionalRestrictionsOnHal(*object, error)) {
             return false;
         }
 #endif
@@ -660,11 +671,11 @@
 
 #ifndef LIBVINTF_TARGET
    private:
-    bool checkAdditionalRestrictionsOnHal(const ManifestHal& hal) const {
+    bool checkAdditionalRestrictionsOnHal(const ManifestHal& hal, std::string* error) const {
         if (hal.getName() == "netutils-wrapper") {
             for (const Version& v : hal.versions) {
                 if (v.minorVer != 0) {
-                    this->mLastError =
+                    *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}) + "'?";
@@ -679,10 +690,10 @@
 
 // Convert ManifestHal from and to XML. Returned object is guaranteed to have
 // .isValid() == true.
-const ManifestHalConverter manifestHalConverter{};
+ManifestHalConverter manifestHalConverter{};
 
-const XmlTextConverter<KernelSepolicyVersion> kernelSepolicyVersionConverter{"kernel-sepolicy-version"};
-const XmlTextConverter<VersionRange> sepolicyVersionConverter{"sepolicy-version"};
+XmlTextConverter<KernelSepolicyVersion> kernelSepolicyVersionConverter{"kernel-sepolicy-version"};
+XmlTextConverter<VersionRange> sepolicyVersionConverter{"sepolicy-version"};
 
 struct SepolicyConverter : public XmlNodeConverter<Sepolicy> {
     std::string elementName() const override { return "sepolicy"; }
@@ -690,21 +701,22 @@
         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)) {
+    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;
     }
 };
-const SepolicyConverter sepolicyConverter{};
+SepolicyConverter sepolicyConverter{};
 
-[[deprecated]]
-const XmlTextConverter<VndkVersionRange> vndkVersionRangeConverter{"version"};
+[[deprecated]] XmlTextConverter<VndkVersionRange> vndkVersionRangeConverter{"version"};
 
-const XmlTextConverter<std::string> vndkVersionConverter{"version"};
-const XmlTextConverter<std::string> vndkLibraryConverter{"library"};
+XmlTextConverter<std::string> vndkVersionConverter{"version"};
+XmlTextConverter<std::string> vndkLibraryConverter{"library"};
 
 struct [[deprecated]] VndkConverter : public XmlNodeConverter<Vndk> {
     std::string elementName() const override { return "vndk"; }
@@ -712,17 +724,16 @@
         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)) {
+    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]]
-const VndkConverter vndkConverter{};
+[[deprecated]] VndkConverter vndkConverter{};
 
 struct VendorNdkConverter : public XmlNodeConverter<VendorNdk> {
     std::string elementName() const override { return "vendor-ndk"; }
@@ -730,41 +741,41 @@
         appendChild(root, vndkVersionConverter(object.mVersion, d));
         appendChildren(root, vndkLibraryConverter, object.mLibraries, d);
     }
-    bool buildObject(VendorNdk* object, NodeType* root) const override {
-        if (!parseChild(root, vndkVersionConverter, &object->mVersion) ||
-            !parseChildren(root, vndkLibraryConverter, &object->mLibraries)) {
+    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;
     }
 };
 
-const VendorNdkConverter vendorNdkConverter{};
+VendorNdkConverter vendorNdkConverter{};
 
-const XmlTextConverter<std::string> systemSdkVersionConverter{"version"};
+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) const override {
-        return parseChildren(root, systemSdkVersionConverter, &object->mVersions);
+    bool buildObject(SystemSdk* object, NodeType* root, std::string* error) const override {
+        return parseChildren(root, systemSdkVersionConverter, &object->mVersions, error);
     }
 };
 
-const SystemSdkConverter systemSdkConverter{};
+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) const override {
-        return parseChild(root, versionConverter, object);
+    bool buildObject(Version* object, NodeType* root, std::string* error) const override {
+        return parseChild(root, versionConverter, object, error);
     }
 };
-const HalManifestSepolicyConverter halManifestSepolicyConverter{};
+HalManifestSepolicyConverter halManifestSepolicyConverter{};
 
 struct ManifestXmlFileConverter : public XmlNodeConverter<ManifestXmlFile> {
     std::string elementName() const override { return "xmlfile"; }
@@ -775,16 +786,16 @@
             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)) {
+    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;
     }
 };
-const ManifestXmlFileConverter manifestXmlFileConverter{};
+ManifestXmlFileConverter manifestXmlFileConverter{};
 
 struct HalManifestConverter : public XmlNodeConverter<HalManifest> {
     std::string elementName() const override { return "manifest"; }
@@ -826,17 +837,17 @@
             appendChildren(root, manifestXmlFileConverter, m.getXmlFiles(), d);
         }
     }
-    bool buildObject(HalManifest *object, NodeType *root) const override {
+    bool buildObject(HalManifest* object, NodeType* root, std::string* error) const override {
         std::vector<ManifestHal> hals;
-        if (!parseAttr(root, "version", &object->mMetaVersion) ||
-            !parseAttr(root, "type", &object->mType) ||
-            !parseOptionalAttr(root, "target-level", Level::UNSPECIFIED, &object->mLevel) ||
-            !parseChildren(root, manifestHalConverter, &hals)) {
+        if (!parseAttr(root, "version", &object->mMetaVersion, error) ||
+            !parseAttr(root, "type", &object->mType, error) ||
+            !parseOptionalAttr(root, "target-level", Level::UNSPECIFIED, &object->mLevel, error) ||
+            !parseChildren(root, manifestHalConverter, &hals, error)) {
             return false;
         }
         if (!kMetaVersion.minorAtLeast(object->mMetaVersion)) {
-            this->mLastError = "Unrecognized manifest.version " + to_string(object->mMetaVersion) +
-                               " (libvintf@" + to_string(kMetaVersion) + ")";
+            *error = "Unrecognized manifest.version " + to_string(object->mMetaVersion) +
+                     " (libvintf@" + to_string(kMetaVersion) + ")";
             return false;
         }
         if (object->mType == SchemaType::DEVICE) {
@@ -844,59 +855,59 @@
             // <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)) {
+                                    &object->device.mSepolicyVersion, 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)) {
+            if (!parseChildren(root, vndkConverter, &object->framework.mVndks, error)) {
                 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";
+                    *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)) {
+            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()) {
-                    this->mLastError =
-                        "Duplicated manifest.vendor-ndk.version " + vendorNdk.version();
+                    *error = "Duplicated manifest.vendor-ndk.version " + vendorNdk.version();
                     return false;
                 }
                 vendorNdkVersions.insert(vendorNdk.version());
             }
 
-            if (!parseOptionalChild(root, systemSdkConverter, {}, &object->framework.mSystemSdk)) {
+            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))) {
-                this->mLastError = "Duplicated manifest.hal entry " + description;
+                *error = "Duplicated manifest.hal entry " + description;
                 return false;
             }
         }
 
         std::vector<ManifestXmlFile> xmlFiles;
-        if (!parseChildren(root, manifestXmlFileConverter, &xmlFiles)) {
+        if (!parseChildren(root, manifestXmlFileConverter, &xmlFiles, error)) {
             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";
+                *error = "Duplicated manifest.xmlfile entry " + description +
+                         "; entries cannot have duplicated name and version";
                 return false;
             }
         }
@@ -905,19 +916,19 @@
     }
 };
 
-const HalManifestConverter halManifestConverter{};
+HalManifestConverter halManifestConverter{};
 
-const XmlTextConverter<Version> avbVersionConverter{"vbmeta-version"};
+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);
+    bool buildObject(Version* object, NodeType* root, std::string* error) const override {
+        return parseChild(root, avbVersionConverter, object, error);
     }
 };
-const AvbConverter avbConverter{};
+AvbConverter avbConverter{};
 
 struct MatrixXmlFileConverter : public XmlNodeConverter<MatrixXmlFile> {
     std::string elementName() const override { return "xmlfile"; }
@@ -930,18 +941,18 @@
             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)) {
+    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;
     }
 };
-const MatrixXmlFileConverter matrixXmlFileConverter{};
+MatrixXmlFileConverter matrixXmlFileConverter{};
 
 struct CompatibilityMatrixConverter : public XmlNodeConverter<CompatibilityMatrix> {
     std::string elementName() const override { return "compatibility-matrix"; }
@@ -998,21 +1009,25 @@
             appendChildren(root, matrixXmlFileConverter, m.getXmlFiles(), d);
         }
     }
-    bool buildObject(CompatibilityMatrix *object, NodeType *root) const override {
+    bool buildObject(CompatibilityMatrix* object, NodeType* root,
+                     std::string* error) const override {
         Version version;
         std::vector<MatrixHal> hals;
-        if (!parseAttr(root, "version", &version) || !parseAttr(root, "type", &object->mType) ||
-            !parseOptionalAttr(root, "level", Level::UNSPECIFIED, &object->mLevel) ||
-            !parseChildren(root, matrixHalConverter, &hals)) {
+        if (!parseAttr(root, "version", &version, error) ||
+            !parseAttr(root, "type", &object->mType, error) ||
+            !parseOptionalAttr(root, "level", Level::UNSPECIFIED, &object->mLevel, 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) ||
-                !parseOptionalChild(root, sepolicyConverter, {}, &object->framework.mSepolicy) ||
-                !parseOptionalChild(root, avbConverter, {}, &object->framework.mAvbMetaVersion)) {
+            if (!parseChildren(root, matrixKernelConverter, &object->framework.mKernels, error) ||
+                !parseOptionalChild(root, sepolicyConverter, {}, &object->framework.mSepolicy,
+                                    error) ||
+                !parseOptionalChild(root, avbConverter, {}, &object->framework.mAvbMetaVersion,
+                                    error)) {
                 return false;
             }
 
@@ -1023,8 +1038,8 @@
                     continue;
                 }
                 if (!kernel.conditions().empty()) {
-                    this->mLastError = "First <kernel> for version " + to_string(minLts) +
-                                       " must have empty <conditions> for backwards compatibility.";
+                    *error = "First <kernel> for version " + to_string(minLts) +
+                             " must have empty <conditions> for backwards compatibility.";
                     return false;
                 }
                 seenKernelVersions.insert(minLts);
@@ -1035,45 +1050,47 @@
             // in the XML file.
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
-            if (!parseOptionalChild(root, vndkConverter, {}, &object->device.mVndk)) {
+            if (!parseOptionalChild(root, vndkConverter, {}, &object->device.mVndk, error)) {
                 return false;
             }
 #pragma clang diagnostic pop
 
-            if (!parseOptionalChild(root, vendorNdkConverter, {}, &object->device.mVendorNdk)) {
+            if (!parseOptionalChild(root, vendorNdkConverter, {}, &object->device.mVendorNdk,
+                                    error)) {
                 return false;
             }
 
-            if (!parseOptionalChild(root, systemSdkConverter, {}, &object->device.mSystemSdk)) {
+            if (!parseOptionalChild(root, systemSdkConverter, {}, &object->device.mSystemSdk,
+                                    error)) {
                 return false;
             }
         }
 
         if (!kMetaVersion.minorAtLeast(version)) {
-            this->mLastError = "Unrecognized compatibility-matrix.version " + to_string(version) +
-                               " (libvintf@" + to_string(kMetaVersion) + ")";
+            *error = "Unrecognized compatibility-matrix.version " + to_string(version) +
+                     " (libvintf@" + to_string(kMetaVersion) + ")";
             return false;
         }
         for (auto &&hal : hals) {
             if (!object->add(std::move(hal))) {
-                this->mLastError = "Duplicated compatibility-matrix.hal entry";
+                *error = "Duplicated compatibility-matrix.hal entry";
                 return false;
             }
         }
 
         std::vector<MatrixXmlFile> xmlFiles;
-        if (!parseChildren(root, matrixXmlFileConverter, &xmlFiles)) {
+        if (!parseChildren(root, matrixXmlFileConverter, &xmlFiles, error)) {
             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";
+                *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))) {
-                this->mLastError = "Duplicated compatibility-matrix.xmlfile entry " + description;
+                *error = "Duplicated compatibility-matrix.xmlfile entry " + description;
                 return false;
             }
         }
@@ -1082,19 +1099,18 @@
     }
 };
 
-const CompatibilityMatrixConverter compatibilityMatrixConverter{};
+CompatibilityMatrixConverter compatibilityMatrixConverter{};
 
 // Publicly available as in parse_xml.h
-const XmlConverter<HalManifest> &gHalManifestConverter = halManifestConverter;
-const XmlConverter<CompatibilityMatrix> &gCompatibilityMatrixConverter
-        = compatibilityMatrixConverter;
+XmlConverter<HalManifest>& gHalManifestConverter = halManifestConverter;
+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;
+XmlConverter<Version>& gVersionConverter = versionConverter;
+XmlConverter<KernelConfigTypedValue>& gKernelConfigTypedValueConverter =
+    kernelConfigTypedValueConverter;
+XmlConverter<MatrixHal>& gMatrixHalConverter = matrixHalConverter;
+XmlConverter<ManifestHal>& gManifestHalConverter = manifestHalConverter;
 
 } // namespace vintf
 } // namespace android