Merge "Add releasetools_test to TEST_MAPPING."
diff --git a/AssembleVintf.cpp b/AssembleVintf.cpp
index a954acd..71ea1bc 100644
--- a/AssembleVintf.cpp
+++ b/AssembleVintf.cpp
@@ -47,10 +47,12 @@
 // It takes ownership on the istream.
 class NamedIstream {
    public:
+    NamedIstream() = default;
     NamedIstream(const std::string& name, std::unique_ptr<std::istream>&& stream)
         : mName(name), mStream(std::move(stream)) {}
     const std::string& name() const { return mName; }
     std::istream& stream() { return *mStream; }
+    bool hasStream() { return mStream != nullptr; }
 
    private:
     std::string mName;
@@ -294,18 +296,16 @@
         return true;
     }
 
-    template <typename S>
-    using Schemas = std::vector<Named<S>>;
-    using HalManifests = Schemas<HalManifest>;
-    using CompatibilityMatrices = Schemas<CompatibilityMatrix>;
+    using HalManifests = std::vector<HalManifest>;
+    using CompatibilityMatrices = std::vector<CompatibilityMatrix>;
 
     template <typename M>
-    void outputInputs(const Schemas<M>& inputs) {
+    void outputInputs(const std::vector<M>& inputs) {
         out() << "<!--" << std::endl;
         out() << "    Input:" << std::endl;
         for (const auto& e : inputs) {
-            if (!e.name.empty()) {
-                out() << "        " << base::Basename(e.name) << std::endl;
+            if (!e.fileName().empty()) {
+                out() << "        " << base::Basename(e.fileName()) << std::endl;
             }
         }
         out() << "-->" << std::endl;
@@ -364,17 +364,17 @@
 
     bool assembleHalManifest(HalManifests* halManifests) {
         std::string error;
-        HalManifest* halManifest = &halManifests->front().object;
+        HalManifest* halManifest = &halManifests->front();
         for (auto it = halManifests->begin() + 1; it != halManifests->end(); ++it) {
-            const std::string& path = it->name;
-            HalManifest& manifestToAdd = it->object;
+            const std::string& path = it->fileName();
+            HalManifest& manifestToAdd = *it;
 
             if (manifestToAdd.level() != Level::UNSPECIFIED) {
                 if (halManifest->level() == Level::UNSPECIFIED) {
                     halManifest->mLevel = manifestToAdd.level();
                 } else if (halManifest->level() != manifestToAdd.level()) {
                     std::cerr << "Inconsistent FCM Version in HAL manifests:" << std::endl
-                              << "    File '" << halManifests->front().name << "' has level "
+                              << "    File '" << halManifests->front().fileName() << "' has level "
                               << halManifest->level() << std::endl
                               << "    File '" << path << "' has level " << manifestToAdd.level()
                               << std::endl;
@@ -435,9 +435,10 @@
         }
         out().flush();
 
-        if (mCheckFile != nullptr) {
+        if (mCheckFile.hasStream()) {
             CompatibilityMatrix checkMatrix;
-            if (!gCompatibilityMatrixConverter(&checkMatrix, read(*mCheckFile), &error)) {
+            checkMatrix.setFileName(mCheckFile.name());
+            if (!gCompatibilityMatrixConverter(&checkMatrix, read(mCheckFile.stream()), &error)) {
                 std::cerr << "Cannot parse check file as a compatibility matrix: " << error
                           << std::endl;
                 return false;
@@ -516,8 +517,8 @@
     Level getLowestFcmVersion(const CompatibilityMatrices& matrices) {
         Level ret = Level::UNSPECIFIED;
         for (const auto& e : matrices) {
-            if (ret == Level::UNSPECIFIED || ret > e.object.level()) {
-                ret = e.object.level();
+            if (ret == Level::UNSPECIFIED || ret > e.level()) {
+                ret = e.level();
             }
         }
         return ret;
@@ -529,15 +530,16 @@
         std::unique_ptr<HalManifest> checkManifest;
         std::unique_ptr<CompatibilityMatrix> builtMatrix;
 
-        if (mCheckFile != nullptr) {
+        if (mCheckFile.hasStream()) {
             checkManifest = std::make_unique<HalManifest>();
-            if (!gHalManifestConverter(checkManifest.get(), read(*mCheckFile), &error)) {
+            checkManifest->setFileName(mCheckFile.name());
+            if (!gHalManifestConverter(checkManifest.get(), read(mCheckFile.stream()), &error)) {
                 std::cerr << "Cannot parse check file as a HAL manifest: " << error << std::endl;
                 return false;
             }
         }
 
-        if (matrices->front().object.mType == SchemaType::DEVICE) {
+        if (matrices->front().mType == SchemaType::DEVICE) {
             builtMatrix = CompatibilityMatrix::combineDeviceMatrices(matrices, &error);
             matrix = builtMatrix.get();
 
@@ -551,7 +553,7 @@
                 auto& valueInMatrix = matrix->device.mVendorNdk;
                 if (!valueInMatrix.version().empty() && valueInMatrix.version() != vndkVersion) {
                     std::cerr << "Hard-coded <vendor-ndk> version in device compatibility matrix ("
-                              << matrices->front().name << "), '" << valueInMatrix.version()
+                              << matrices->front().fileName() << "), '" << valueInMatrix.version()
                               << "', does not match value inferred "
                               << "from BOARD_VNDK_VERSION '" << vndkVersion << "'" << std::endl;
                     return false;
@@ -564,7 +566,7 @@
             }
         }
 
-        if (matrices->front().object.mType == SchemaType::FRAMEWORK) {
+        if (matrices->front().mType == SchemaType::FRAMEWORK) {
             Level deviceLevel =
                 checkManifest != nullptr ? checkManifest->level() : Level::UNSPECIFIED;
             if (deviceLevel == Level::UNSPECIFIED) {
@@ -638,17 +640,19 @@
     template <typename Schema, typename AssembleFunc>
     AssembleStatus tryAssemble(const XmlConverter<Schema>& converter, const std::string& schemaName,
                                AssembleFunc assemble, std::string* error) {
-        Schemas<Schema> schemas;
+        std::vector<Schema> schemas;
         Schema schema;
+        schema.setFileName(mInFiles.front().name());
         if (!converter(&schema, read(mInFiles.front().stream()), error)) {
             return TRY_NEXT;
         }
         auto firstType = schema.type();
-        schemas.emplace_back(mInFiles.front().name(), std::move(schema));
+        schemas.emplace_back(std::move(schema));
 
         for (auto it = mInFiles.begin() + 1; it != mInFiles.end(); ++it) {
             Schema additionalSchema;
             const std::string& fileName = it->name();
+            additionalSchema.setFileName(fileName);
             if (!converter(&additionalSchema, read(it->stream()), error)) {
                 std::cerr << "File \"" << fileName << "\" is not a valid " << firstType << " "
                           << schemaName << " (but the first file is a valid " << firstType << " "
@@ -662,7 +666,7 @@
                 return FAIL_AND_EXIT;
             }
 
-            schemas.emplace_back(fileName, std::move(additionalSchema));
+            schemas.emplace_back(std::move(additionalSchema));
         }
         return assemble(&schemas) ? SUCCESS : FAIL_AND_EXIT;
     }
@@ -707,9 +711,9 @@
         return it->stream();
     }
 
-    std::istream& setCheckInputStream(Istream&& in) override {
-        mCheckFile = std::move(in);
-        return *mCheckFile;
+    std::istream& setCheckInputStream(const std::string& name, Istream&& in) override {
+        mCheckFile = NamedIstream(name, std::move(in));
+        return mCheckFile.stream();
     }
 
     bool hasKernelVersion(const KernelVersion& kernelVer) const override {
@@ -763,7 +767,7 @@
    private:
     std::vector<NamedIstream> mInFiles;
     Ostream mOutRef;
-    Istream mCheckFile;
+    NamedIstream mCheckFile;
     bool mOutputMatrix = false;
     bool mHasSetHalsOnlyFlag = false;
     SerializeFlags::Type mSerializeFlags = SerializeFlags::EVERYTHING;
@@ -783,7 +787,8 @@
 }
 
 bool AssembleVintf::openCheckFile(const std::string& path) {
-    return static_cast<std::ifstream&>(setCheckInputStream(std::make_unique<std::ifstream>(path)))
+    return static_cast<std::ifstream&>(
+               setCheckInputStream(path, std::make_unique<std::ifstream>(path)))
         .is_open();
 }
 
diff --git a/CompatibilityMatrix.cpp b/CompatibilityMatrix.cpp
index a2ef873..8ca2dbd 100644
--- a/CompatibilityMatrix.cpp
+++ b/CompatibilityMatrix.cpp
@@ -31,6 +31,20 @@
 
 using details::mergeField;
 
+bool CompatibilityMatrix::add(MatrixHal&& halToAdd, std::string*) {
+    CHECK(addInternal(std::move(halToAdd)) != nullptr);
+    return true;
+}
+
+bool CompatibilityMatrix::addAllHals(CompatibilityMatrix* other, std::string*) {
+    std::string internalError;
+    for (auto& pair : other->mHals) {
+        CHECK(add(std::move(pair.second), &internalError)) << internalError;
+    }
+    other->mHals.clear();
+    return true;
+}
+
 bool CompatibilityMatrix::addKernel(MatrixKernel&& kernel, std::string* error) {
     if (mType != SchemaType::FRAMEWORK) {
         if (error) {
@@ -303,6 +317,7 @@
 }
 
 bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) {
+    // ignore fileName().
     return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals &&
            lft.mXmlFiles == rgt.mXmlFiles &&
            (lft.mType != SchemaType::DEVICE ||
@@ -320,12 +335,12 @@
 }
 
 std::unique_ptr<CompatibilityMatrix> CompatibilityMatrix::combine(
-    Level deviceLevel, std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error) {
+    Level deviceLevel, std::vector<CompatibilityMatrix>* matrices, std::string* error) {
     // Check type.
     for (const auto& e : *matrices) {
-        if (e.object.type() != SchemaType::FRAMEWORK) {
+        if (e.type() != SchemaType::FRAMEWORK) {
             if (error) {
-                *error = "File \"" + e.name + "\" is not a framework compatibility matrix.";
+                *error = "File \"" + e.fileName() + "\" is not a framework compatibility matrix.";
                 return nullptr;
             }
         }
@@ -333,15 +348,15 @@
 
     // Matrices with unspecified (empty) level are auto-filled with deviceLevel.
     for (auto& e : *matrices) {
-        if (e.object.level() == Level::UNSPECIFIED) {
-            e.object.mLevel = deviceLevel;
+        if (e.level() == Level::UNSPECIFIED) {
+            e.mLevel = deviceLevel;
         }
     }
 
     // Add from low to high FCM version so that optional <kernel> requirements are added correctly.
     // See comment in addAllAsOptional.
     std::sort(matrices->begin(), matrices->end(),
-              [](const auto& x, const auto& y) { return x.object.level() < y.object.level(); });
+              [](const auto& x, const auto& y) { return x.level() < y.level(); });
 
     auto baseMatrix = std::make_unique<CompatibilityMatrix>();
     baseMatrix->mLevel = deviceLevel;
@@ -349,31 +364,31 @@
 
     std::vector<std::string> parsedFiles;
     for (auto& e : *matrices) {
-        if (e.object.level() < deviceLevel) {
+        if (e.level() < deviceLevel) {
             continue;
         }
 
         bool success = false;
-        if (e.object.level() == deviceLevel) {
+        if (e.level() == deviceLevel) {
             success = baseMatrix->addAll(&e, error);
         } else {
             success = baseMatrix->addAllAsOptional(&e, error);
         }
         if (!success) {
             if (error) {
-                *error = "Conflict when merging \"" + e.name + "\": " + *error + "\n" +
+                *error = "Conflict when merging \"" + e.fileName() + "\": " + *error + "\n" +
                          "Previous files:\n" + base::Join(parsedFiles, "\n");
             }
             return nullptr;
         }
-        parsedFiles.push_back(e.name);
+        parsedFiles.push_back(e.fileName());
     }
 
     return baseMatrix;
 }
 
 std::unique_ptr<CompatibilityMatrix> CompatibilityMatrix::combineDeviceMatrices(
-    std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error) {
+    std::vector<CompatibilityMatrix>* matrices, std::string* error) {
     auto baseMatrix = std::make_unique<CompatibilityMatrix>();
     baseMatrix->mType = SchemaType::DEVICE;
 
@@ -382,36 +397,34 @@
         bool success = baseMatrix->addAll(&e, error);
         if (!success) {
             if (error) {
-                *error = "Conflict when merging \"" + e.name + "\": " + *error + "\n" +
+                *error = "Conflict when merging \"" + e.fileName() + "\": " + *error + "\n" +
                          "Previous files:\n" + base::Join(parsedFiles, "\n");
             }
             return nullptr;
         }
-        parsedFiles.push_back(e.name);
+        parsedFiles.push_back(e.fileName());
     }
     return baseMatrix;
 }
 
-bool CompatibilityMatrix::addAll(Named<CompatibilityMatrix>* inputMatrix, std::string* error) {
-    if (!addAllHals(&inputMatrix->object, error) || !addAllXmlFiles(&inputMatrix->object, error) ||
-        !addAllKernels(&inputMatrix->object, error) || !addSepolicy(&inputMatrix->object, error) ||
-        !addAvbMetaVersion(&inputMatrix->object, error) || !addVndk(&inputMatrix->object, error) ||
-        !addVendorNdk(&inputMatrix->object, error) || !addSystemSdk(&inputMatrix->object, error)) {
+bool CompatibilityMatrix::addAll(CompatibilityMatrix* inputMatrix, std::string* error) {
+    if (!addAllHals(inputMatrix, error) || !addAllXmlFiles(inputMatrix, error) ||
+        !addAllKernels(inputMatrix, error) || !addSepolicy(inputMatrix, error) ||
+        !addAvbMetaVersion(inputMatrix, error) || !addVndk(inputMatrix, error) ||
+        !addVendorNdk(inputMatrix, error) || !addSystemSdk(inputMatrix, error)) {
         if (error) {
-            *error = "File \"" + inputMatrix->name + "\" cannot be added: " + *error + ".";
+            *error = "File \"" + inputMatrix->fileName() + "\" cannot be added: " + *error + ".";
         }
         return false;
     }
     return true;
 }
 
-bool CompatibilityMatrix::addAllAsOptional(Named<CompatibilityMatrix>* inputMatrix,
-                                           std::string* error) {
-    if (!addAllHalsAsOptional(&inputMatrix->object, error) ||
-        !addAllXmlFilesAsOptional(&inputMatrix->object, error) ||
-        !addAllKernels(&inputMatrix->object, error)) {
+bool CompatibilityMatrix::addAllAsOptional(CompatibilityMatrix* inputMatrix, std::string* error) {
+    if (!addAllHalsAsOptional(inputMatrix, error) ||
+        !addAllXmlFilesAsOptional(inputMatrix, error) || !addAllKernels(inputMatrix, error)) {
         if (error) {
-            *error = "File \"" + inputMatrix->name + "\" cannot be added: " + *error;
+            *error = "File \"" + inputMatrix->fileName() + "\" cannot be added: " + *error;
         }
         return false;
     }
diff --git a/HalManifest.cpp b/HalManifest.cpp
index 9118773..3fcbc22 100644
--- a/HalManifest.cpp
+++ b/HalManifest.cpp
@@ -41,27 +41,50 @@
 using details::mergeField;
 
 // Check <version> tag for all <hal> with the same name.
-bool HalManifest::shouldAdd(const ManifestHal& hal) const {
-    if (!hal.isValid()) {
+bool HalManifest::shouldAdd(const ManifestHal& hal, std::string* error) const {
+    if (!hal.isValid(error)) {
+        if (error) {
+            error->insert(0, "HAL '" + hal.name + "' is not valid: ");
+            if (!hal.fileName().empty()) {
+                error->insert(0, "For file " + hal.fileName() + ": ");
+            }
+        }
         return false;
     }
     if (hal.isOverride()) {
         return true;
     }
     auto existingHals = mHals.equal_range(hal.name);
-    std::set<size_t> existingMajorVersions;
+    std::map<size_t, std::tuple<const ManifestHal*, Version>> existing;
     for (auto it = existingHals.first; it != existingHals.second; ++it) {
-        for (const auto& v : it->second.versions) {
+        const ManifestHal& existingHal = it->second;
+        for (const auto& v : existingHal.versions) {
             // Assume integrity on existingHals, so no check on emplace().second
-            existingMajorVersions.insert(v.majorVer);
+            existing.emplace(v.majorVer, std::make_tuple(&existingHal, v));
         }
     }
+    bool success = true;
     for (const auto& v : hal.versions) {
-        if (!existingMajorVersions.emplace(v.majorVer).second /* no insertion */) {
-            return false;
+        auto&& [existingIt, inserted] = existing.emplace(v.majorVer, std::make_tuple(&hal, v));
+        if (inserted) {
+            continue;
+        }
+        success = false;
+        if (error) {
+            auto&& [existingHal, existingVersion] = existingIt->second;
+            *error = "Conflicting major version: " + to_string(existingVersion);
+            if (!existingHal->fileName().empty()) {
+                *error += " (from " + existingHal->fileName() + ")";
+            }
+            *error += " vs. " + to_string(v);
+            if (!hal.fileName().empty()) {
+                *error += " (from " + hal.fileName() + ")";
+            }
+            *error +=
+                ". Check whether or not multiple modules providing the same HAL are installed.";
         }
     }
-    return true;
+    return success;
 }
 
 // Remove elements from "list" if p(element) returns true.
@@ -90,7 +113,7 @@
     });
 }
 
-bool HalManifest::add(ManifestHal&& halToAdd) {
+bool HalManifest::add(ManifestHal&& halToAdd, std::string* error) {
     if (halToAdd.isOverride()) {
         if (halToAdd.isDisabledHal()) {
             // Special syntax when there are no instances at all. Remove all existing HALs
@@ -103,7 +126,25 @@
         }
     }
 
-    return HalGroup::add(std::move(halToAdd));
+    if (!shouldAdd(halToAdd, error)) {
+        return false;
+    }
+
+    CHECK(addInternal(std::move(halToAdd)) != nullptr);
+    return true;
+}
+
+bool HalManifest::addAllHals(HalManifest* other, std::string* error) {
+    for (auto& pair : other->mHals) {
+        if (!add(std::move(pair.second), error)) {
+            if (error) {
+                error->insert(0, "HAL \"" + pair.first + "\" has a conflict: ");
+            }
+            return false;
+        }
+    }
+    other->mHals.clear();
+    return true;
 }
 
 bool HalManifest::shouldAddXmlFile(const ManifestXmlFile& xmlFile) const {
@@ -465,6 +506,7 @@
 }
 
 bool operator==(const HalManifest &lft, const HalManifest &rgt) {
+    // ignore fileName().
     return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals &&
            lft.mXmlFiles == rgt.mXmlFiles &&
            (lft.mType != SchemaType::DEVICE ||
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/ManifestHal.cpp b/ManifestHal.cpp
index 3a2a1c0..3fdc6e2 100644
--- a/ManifestHal.cpp
+++ b/ManifestHal.cpp
@@ -24,18 +24,33 @@
 namespace android {
 namespace vintf {
 
-bool ManifestHal::isValid() const {
-    std::unordered_set<size_t> existing;
-    for (const auto &v : versions) {
-        if (existing.find(v.majorVer) != existing.end()) {
-            return false;
-        }
-        existing.insert(v.majorVer);
+bool ManifestHal::isValid(std::string* error) const {
+    if (error) {
+        error->clear();
     }
-    return transportArch.isValid();
+
+    bool success = true;
+    std::map<size_t, Version> existing;
+    for (const auto &v : versions) {
+        auto&& [it, inserted] = existing.emplace(v.majorVer, v);
+        if (inserted) {
+            continue;
+        }
+        success = false;
+        if (error) {
+            *error += "Duplicated major version: " + to_string(v) + " vs. " + to_string(it->second);
+        }
+    }
+    std::string transportArchError;
+    if (!transportArch.isValid(&transportArchError)) {
+        success = false;
+        if (error) *error += transportArchError;
+    }
+    return success;
 }
 
 bool ManifestHal::operator==(const ManifestHal &other) const {
+    // ignore fileName().
     if (format != other.format)
         return false;
     if (name != other.name)
diff --git a/TransportArch.cpp b/TransportArch.cpp
index 7fb63c3..19c1dcd 100644
--- a/TransportArch.cpp
+++ b/TransportArch.cpp
@@ -17,6 +17,8 @@
 
 #include "TransportArch.h"
 
+#include "parse_string.h"
+
 namespace android {
 namespace vintf {
 
@@ -24,14 +26,30 @@
     return transport == Transport::EMPTY && arch == Arch::ARCH_EMPTY;
 }
 
-bool TransportArch::isValid() const {
+bool TransportArch::isValid(std::string* error) const {
+    if (error) {
+        error->clear();
+    }
+
     switch (transport) {
         case Transport::EMPTY: // fallthrough
         case Transport::HWBINDER:
-            return arch == Arch::ARCH_EMPTY;
+            if (arch == Arch::ARCH_EMPTY) {
+                return true;
+            }
+            if (error) {
+                *error += "Transport " + to_string(transport) + " requires empty arch attribute";
+            }
+            return false;
 
         case Transport::PASSTHROUGH:
-            return arch != Arch::ARCH_EMPTY;
+            if (arch != Arch::ARCH_EMPTY) {
+                return true;
+            }
+            if (error) {
+                *error += "Passthrough HALs requires arch attribute";
+            }
+            return false;
     }
 }
 
diff --git a/VintfObject.cpp b/VintfObject.cpp
index 2611a56..c6ee42d 100644
--- a/VintfObject.cpp
+++ b/VintfObject.cpp
@@ -48,20 +48,24 @@
 #endif
 
 template <typename T, typename F>
-static std::shared_ptr<const T> Get(
-        LockedSharedPtr<T> *ptr,
-        bool skipCache,
-        const F &fetchAllInformation) {
+static std::shared_ptr<const T> Get(const char* id, LockedSharedPtr<T>* ptr, bool skipCache,
+                                    const F& fetchAllInformation) {
     std::unique_lock<std::mutex> _lock(ptr->mutex);
     if (skipCache || !ptr->fetchedOnce) {
+        LOG(INFO) << id << ": Reading VINTF information.";
         ptr->object = std::make_unique<T>();
         std::string error;
         status_t status = fetchAllInformation(ptr->object.get(), &error);
-        if (status != OK) {
-            LOG(WARNING) << status << " VINTF parse error: " << error;
+        if (status == OK) {
+            ptr->fetchedOnce = true;
+            LOG(INFO) << id << ": Successfully processed VINTF information";
+        } else {
+            // Doubled because a malformed error std::string might cause us to
+            // lose the status.
+            LOG(ERROR) << id << ": status from fetching VINTF information: " << status;
+            LOG(ERROR) << id << ": " << status << " VINTF parse error: " << error;
             ptr->object = nullptr; // frees the old object
         }
-        ptr->fetchedOnce = true;
     }
     return ptr->object;
 }
@@ -100,7 +104,7 @@
 }
 
 std::shared_ptr<const HalManifest> VintfObject::getDeviceHalManifest(bool skipCache) {
-    return Get(&mDeviceManifest, skipCache,
+    return Get(__func__, &mDeviceManifest, skipCache,
                std::bind(&VintfObject::fetchDeviceHalManifest, this, _1, _2));
 }
 
@@ -109,7 +113,7 @@
 }
 
 std::shared_ptr<const HalManifest> VintfObject::getFrameworkHalManifest(bool skipCache) {
-    return Get(&mFrameworkManifest, skipCache,
+    return Get(__func__, &mFrameworkManifest, skipCache,
                std::bind(&VintfObject::fetchFrameworkHalManifest, this, _1, _2));
 }
 
@@ -119,7 +123,8 @@
 
 std::shared_ptr<const CompatibilityMatrix> VintfObject::getDeviceCompatibilityMatrix(
     bool skipCache) {
-    return Get(&mDeviceMatrix, skipCache, std::bind(&VintfObject::fetchDeviceMatrix, this, _1, _2));
+    return Get(__func__, &mDeviceMatrix, skipCache,
+               std::bind(&VintfObject::fetchDeviceMatrix, this, _1, _2));
 }
 
 std::shared_ptr<const CompatibilityMatrix> VintfObject::GetFrameworkCompatibilityMatrix(bool skipCache) {
@@ -134,13 +139,13 @@
     std::unique_lock<std::mutex> _lock(mFrameworkCompatibilityMatrixMutex);
 
     auto combined =
-        Get(&mCombinedFrameworkMatrix, skipCache,
+        Get(__func__, &mCombinedFrameworkMatrix, skipCache,
             std::bind(&VintfObject::getCombinedFrameworkMatrix, this, deviceManifest, _1, _2));
     if (combined != nullptr) {
         return combined;
     }
 
-    return Get(&mFrameworkMatrix, skipCache,
+    return Get(__func__, &mFrameworkMatrix, skipCache,
                std::bind(&CompatibilityMatrix::fetchAllInformation, _1, getFileSystem().get(),
                          kSystemLegacyMatrix, _2));
 }
@@ -148,7 +153,7 @@
 status_t VintfObject::getCombinedFrameworkMatrix(
     const std::shared_ptr<const HalManifest>& deviceManifest, CompatibilityMatrix* out,
     std::string* error) {
-    std::vector<Named<CompatibilityMatrix>> matrixFragments;
+    std::vector<CompatibilityMatrix> matrixFragments;
     auto matrixFragmentsStatus = getAllFrameworkMatrixLevels(&matrixFragments, error);
     if (matrixFragmentsStatus != OK) {
         return matrixFragmentsStatus;
@@ -177,8 +182,8 @@
     if (deviceLevel == Level::UNSPECIFIED) {
         // Cannot infer FCM version. Combine all matrices by assuming
         // Shipping FCM Version == min(all supported FCM Versions in the framework)
-        for (auto&& pair : matrixFragments) {
-            Level fragmentLevel = pair.object.level();
+        for (auto&& fragment : matrixFragments) {
+            Level fragmentLevel = fragment.level();
             if (fragmentLevel != Level::UNSPECIFIED && deviceLevel > fragmentLevel) {
                 deviceLevel = fragmentLevel;
             }
@@ -221,7 +226,7 @@
 
         if (!manifest->addAll(&fragmentManifest, error)) {
             if (error) {
-                error->insert(0, "Cannot add manifest fragment " + directory + file + ":");
+                error->insert(0, "Cannot add manifest fragment " + directory + file + ": ");
             }
             return UNKNOWN_ERROR;
         }
@@ -426,24 +431,24 @@
     }
 }
 
-status_t VintfObject::getOneMatrix(const std::string& path, Named<CompatibilityMatrix>* out,
+status_t VintfObject::getOneMatrix(const std::string& path, CompatibilityMatrix* out,
                                    std::string* error) {
     std::string content;
     status_t status = getFileSystem()->fetch(path, &content, error);
     if (status != OK) {
         return status;
     }
-    if (!gCompatibilityMatrixConverter(&out->object, content, error)) {
+    if (!gCompatibilityMatrixConverter(out, content, error)) {
         if (error) {
             error->insert(0, "Cannot parse " + path + ": ");
         }
         return BAD_VALUE;
     }
-    out->name = path;
+    out->setFileName(path);
     return OK;
 }
 
-status_t VintfObject::getAllFrameworkMatrixLevels(std::vector<Named<CompatibilityMatrix>>* results,
+status_t VintfObject::getAllFrameworkMatrixLevels(std::vector<CompatibilityMatrix>* results,
                                                   std::string* error) {
     std::vector<std::string> dirs = {
         kSystemVintfDir,
@@ -461,7 +466,7 @@
         }
         for (const std::string& fileName : fileNames) {
             std::string path = dir + fileName;
-            Named<CompatibilityMatrix> namedMatrix;
+            CompatibilityMatrix namedMatrix;
             std::string matrixError;
             status_t matrixStatus = getOneMatrix(path, &namedMatrix, &matrixError);
             if (matrixStatus != OK) {
@@ -819,7 +824,7 @@
 int32_t VintfObject::checkDeprecation(const ListInstances& listInstances,
                                       const std::vector<HidlInterfaceMetadata>& hidlMetadata,
                                       std::string* error) {
-    std::vector<Named<CompatibilityMatrix>> matrixFragments;
+    std::vector<CompatibilityMatrix> matrixFragments;
     auto matrixFragmentsStatus = getAllFrameworkMatrixLevels(&matrixFragments, error);
     if (matrixFragmentsStatus != OK) {
         return matrixFragmentsStatus;
@@ -843,8 +848,8 @@
 
     const CompatibilityMatrix* targetMatrix = nullptr;
     for (const auto& namedMatrix : matrixFragments) {
-        if (namedMatrix.object.level() == deviceLevel) {
-            targetMatrix = &namedMatrix.object;
+        if (namedMatrix.level() == deviceLevel) {
+            targetMatrix = &namedMatrix;
         }
     }
     if (targetMatrix == nullptr) {
@@ -864,11 +869,10 @@
     // Matrices with unspecified level are considered "current".
     bool isDeprecated = false;
     for (const auto& namedMatrix : matrixFragments) {
-        if (namedMatrix.object.level() == Level::UNSPECIFIED) continue;
-        if (namedMatrix.object.level() >= deviceLevel) continue;
+        if (namedMatrix.level() == Level::UNSPECIFIED) continue;
+        if (namedMatrix.level() >= deviceLevel) continue;
 
-        const auto& oldMatrix = namedMatrix.object;
-        for (const MatrixHal& hal : oldMatrix.getHals()) {
+        for (const MatrixHal& hal : namedMatrix.getHals()) {
             if (IsHalDeprecated(hal, *targetMatrix, listInstances, childrenMap, error)) {
                 isDeprecated = true;
             }
@@ -924,7 +928,7 @@
 }
 
 android::base::Result<bool> VintfObject::hasFrameworkCompatibilityMatrixExtensions() {
-    std::vector<Named<CompatibilityMatrix>> matrixFragments;
+    std::vector<CompatibilityMatrix> matrixFragments;
     std::string error;
     status_t status = getAllFrameworkMatrixLevels(&matrixFragments, &error);
     if (status != OK) {
@@ -933,17 +937,16 @@
     }
     for (const auto& namedMatrix : matrixFragments) {
         // Returns true if product matrix exists.
-        if (android::base::StartsWith(namedMatrix.name, kProductVintfDir)) {
+        if (android::base::StartsWith(namedMatrix.fileName(), kProductVintfDir)) {
             return true;
         }
         // Returns true if system_ext matrix exists.
-        if (android::base::StartsWith(namedMatrix.name, kSystemExtVintfDir)) {
+        if (android::base::StartsWith(namedMatrix.fileName(), kSystemExtVintfDir)) {
             return true;
         }
         // Returns true if device system matrix exists.
-        if (android::base::StartsWith(namedMatrix.name, kSystemVintfDir) &&
-            namedMatrix.object.level() == Level::UNSPECIFIED &&
-            !namedMatrix.object.getHals().empty()) {
+        if (android::base::StartsWith(namedMatrix.fileName(), kSystemVintfDir) &&
+            namedMatrix.level() == Level::UNSPECIFIED && !namedMatrix.getHals().empty()) {
             return true;
         }
     }
diff --git a/check_vintf.cpp b/check_vintf.cpp
index d38d136..bdbbf5b 100644
--- a/check_vintf.cpp
+++ b/check_vintf.cpp
@@ -199,6 +199,7 @@
         return nullptr;
     }
     auto ret = std::make_unique<T>();
+    ret->setFileName(path);
     if (!converter(ret.get(), xml, &error)) {
         LOG(ERROR) << "Cannot parse '" << path << "': " << error;
         return nullptr;
diff --git a/include-test/vintf/AssembleVintf.h b/include-test/vintf/AssembleVintf.h
index 56b4811..03c49fb 100644
--- a/include-test/vintf/AssembleVintf.h
+++ b/include-test/vintf/AssembleVintf.h
@@ -49,7 +49,7 @@
 
     virtual std::ostream& setOutputStream(Ostream&&) = 0;
     virtual std::istream& addInputStream(const std::string& name, Istream&&) = 0;
-    virtual std::istream& setCheckInputStream(Istream&&) = 0;
+    virtual std::istream& setCheckInputStream(const std::string& name, Istream&&) = 0;
     virtual std::istream& addKernelConfigInputStream(const KernelVersion& kernelVer,
                                                      const std::string& name, Istream&& in) = 0;
     virtual void setFakeEnv(const std::string& key, const std::string& value) = 0;
diff --git a/include/vintf/CompatibilityMatrix.h b/include/vintf/CompatibilityMatrix.h
index 6bb8fe6..b598037 100644
--- a/include/vintf/CompatibilityMatrix.h
+++ b/include/vintf/CompatibilityMatrix.h
@@ -30,19 +30,21 @@
 #include "MatrixHal.h"
 #include "MatrixInstance.h"
 #include "MatrixKernel.h"
-#include "Named.h"
 #include "SchemaType.h"
 #include "Sepolicy.h"
 #include "SystemSdk.h"
 #include "VendorNdk.h"
 #include "Vndk.h"
+#include "WithFileName.h"
 #include "XmlFileGroup.h"
 
 namespace android {
 namespace vintf {
 
 // Compatibility matrix defines what hardware does the framework requires.
-struct CompatibilityMatrix : public HalGroup<MatrixHal>, public XmlFileGroup<MatrixXmlFile> {
+struct CompatibilityMatrix : public HalGroup<MatrixHal>,
+                             public XmlFileGroup<MatrixXmlFile>,
+                             public WithFileName {
     // Create a framework compatibility matrix.
     CompatibilityMatrix() : mType(SchemaType::FRAMEWORK) {}
 
@@ -62,6 +64,10 @@
 
     std::string getVendorNdkVersion() const;
 
+    bool add(MatrixHal&&, std::string* error = nullptr) override;
+    // Move all hals from another CompatibilityMatrix to this.
+    bool addAllHals(CompatibilityMatrix* other, std::string* error = nullptr);
+
    protected:
     bool forEachInstanceOfVersion(
         HalFormat format, const std::string& package, const Version& expectVersion,
@@ -69,7 +75,7 @@
 
    private:
     // Add everything in inputMatrix to "this" as requirements.
-    bool addAll(Named<CompatibilityMatrix>* inputMatrix, std::string* error);
+    bool addAll(CompatibilityMatrix* inputMatrix, std::string* error);
 
     // Add all <kernel> from other to "this". Error if there is a conflict.
     bool addAllKernels(CompatibilityMatrix* other, std::string* error);
@@ -93,7 +99,7 @@
     bool addSystemSdk(CompatibilityMatrix* other, std::string* error);
 
     // Add everything in inputMatrix to "this" as optional.
-    bool addAllAsOptional(Named<CompatibilityMatrix>* inputMatrix, std::string* error);
+    bool addAllAsOptional(CompatibilityMatrix* inputMatrix, std::string* error);
 
     // Add all HALs as optional HALs from "other". This function moves MatrixHal objects
     // from "other".
@@ -114,12 +120,13 @@
     //     with lower level()
     //   - <sepolicy>, <avb><vbmeta-version> is ignored
     // Return the combined matrix, nullptr if any error (e.g. conflict of information).
-    static std::unique_ptr<CompatibilityMatrix> combine(
-        Level deviceLevel, std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error);
+    static std::unique_ptr<CompatibilityMatrix> combine(Level deviceLevel,
+                                                        std::vector<CompatibilityMatrix>* matrices,
+                                                        std::string* error);
 
     // Combine a set of device compatibility matrices.
     static std::unique_ptr<CompatibilityMatrix> combineDeviceMatrices(
-        std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error);
+        std::vector<CompatibilityMatrix>* matrices, std::string* error);
 
     status_t fetchAllInformation(const FileSystem* fileSystem, const std::string& path,
                                  std::string* error = nullptr);
diff --git a/include/vintf/HalGroup.h b/include/vintf/HalGroup.h
index db87cec..ba0d418 100644
--- a/include/vintf/HalGroup.h
+++ b/include/vintf/HalGroup.h
@@ -35,22 +35,9 @@
 
    public:
     virtual ~HalGroup() {}
-    // Move all hals from another HalGroup to this.
-    bool addAllHals(HalGroup* other, std::string* error = nullptr) {
-        for (auto& pair : other->mHals) {
-            if (!add(std::move(pair.second))) {
-                if (error) {
-                    *error = "HAL \"" + pair.first + "\" has a conflict.";
-                }
-                return false;
-            }
-        }
-        other->mHals.clear();
-        return true;
-    }
 
     // Add an hal to this HalGroup so that it can be constructed programatically.
-    virtual bool add(Hal&& hal) { return addInternal(std::move(hal)) != nullptr; }
+    virtual bool add(Hal&& hal, std::string* error = nullptr) = 0;
 
    protected:
     // Get all hals with the given name (e.g "android.hardware.camera").
@@ -186,9 +173,6 @@
     // The component name looks like: android.hardware.foo
     std::multimap<std::string, Hal> mHals;
 
-    // override this to filter for add.
-    virtual bool shouldAdd(const Hal&) const { return true; }
-
     // Return an iterable to all Hal objects. Call it as follows:
     // for (const auto& e : vm.getHals()) { }
     ConstMultiMapValueIterable<std::string, Hal> getHals() const { return iterateValues(mHals); }
@@ -210,10 +194,8 @@
         return &(it->second);
     }
 
+    // Helper for "add(Hal)". Returns pointer to inserted object. Never null.
     Hal* addInternal(Hal&& hal) {
-        if (!shouldAdd(hal)) {
-            return nullptr;
-        }
         std::string name = hal.getName();
         auto it = mHals.emplace(std::move(name), std::move(hal));  // always succeeds
         return &it->second;
diff --git a/include/vintf/HalManifest.h b/include/vintf/HalManifest.h
index 41298f0..04489c2 100644
--- a/include/vintf/HalManifest.h
+++ b/include/vintf/HalManifest.h
@@ -39,6 +39,7 @@
 #include "VendorNdk.h"
 #include "Version.h"
 #include "Vndk.h"
+#include "WithFileName.h"
 #include "XmlFileGroup.h"
 
 namespace android {
@@ -55,13 +56,17 @@
 
 // A HalManifest is reported by the hardware and query-able from
 // framework code. This is the API for the framework.
-struct HalManifest : public HalGroup<ManifestHal>, public XmlFileGroup<ManifestXmlFile> {
+struct HalManifest : public HalGroup<ManifestHal>,
+                     public XmlFileGroup<ManifestXmlFile>,
+                     public WithFileName {
    public:
 
     // Construct a device HAL manifest.
     HalManifest() : mType(SchemaType::DEVICE) {}
 
-    bool add(ManifestHal&& hal) override;
+    bool add(ManifestHal&& hal, std::string* error = nullptr) override;
+    // Move all hals from another HalManifest to this.
+    bool addAllHals(HalManifest* other, std::string* error = nullptr);
 
     // Given a component name (e.g. "android.hardware.camera"),
     // return getHal(name)->transport if the component exist and v exactly matches
@@ -142,7 +147,7 @@
 
    protected:
     // Check before add()
-    bool shouldAdd(const ManifestHal& toAdd) const override;
+    bool shouldAdd(const ManifestHal& toAdd, std::string* error) const;
     bool shouldAddXmlFile(const ManifestXmlFile& toAdd) const override;
 
     bool forEachInstanceOfVersion(
@@ -227,7 +232,6 @@
     } framework;
 };
 
-
 } // namespace vintf
 } // namespace android
 
diff --git a/include/vintf/ManifestHal.h b/include/vintf/ManifestHal.h
index 3a798a8..f40566a 100644
--- a/include/vintf/ManifestHal.h
+++ b/include/vintf/ManifestHal.h
@@ -30,12 +30,13 @@
 #include "ManifestInstance.h"
 #include "TransportArch.h"
 #include "Version.h"
+#include "WithFileName.h"
 
 namespace android {
 namespace vintf {
 
 // A component of HalManifest.
-struct ManifestHal {
+struct ManifestHal : public WithFileName {
     using InstanceType = ManifestInstance;
 
     ManifestHal() = default;
@@ -80,7 +81,7 @@
 
     // Whether this hal is a valid one. Note that an empty ManifestHal
     // (constructed via ManifestHal()) is valid.
-    bool isValid() const;
+    bool isValid(std::string* error = nullptr) const;
 
     // Return all versions mentioned by <version>s and <fqname>s.
     void appendAllVersions(std::set<Version>* ret) const;
diff --git a/include/vintf/Named.h b/include/vintf/Named.h
deleted file mode 100644
index eacb70b..0000000
--- a/include/vintf/Named.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_VINTF_NAMED_H
-#define ANDROID_VINTF_NAMED_H
-
-#include <string>
-
-template <typename T>
-struct Named {
-    std::string name;
-    T object;
-
-    Named() = default;
-    Named(const std::string& n, const T& o) : name(n), object(o) {}
-    Named(std::string&& n, T&& o) : name(std::move(n)), object(std::move(o)) {}
-};
-
-#endif  // ANDROID_VINTF_NAMED_H
diff --git a/include/vintf/TransportArch.h b/include/vintf/TransportArch.h
index 51630de..8e892e8 100644
--- a/include/vintf/TransportArch.h
+++ b/include/vintf/TransportArch.h
@@ -52,7 +52,7 @@
     // <transport arch="32+64">passthrough</transport>
     // <transport>hwbinder</transport>
     // Element doesn't exist
-    bool isValid() const;
+    bool isValid(std::string* error = nullptr) const;
 };
 
 
diff --git a/include/vintf/VintfObject.h b/include/vintf/VintfObject.h
index fa25669..4fb19bd 100644
--- a/include/vintf/VintfObject.h
+++ b/include/vintf/VintfObject.h
@@ -32,7 +32,6 @@
 #include "FileSystem.h"
 #include "HalManifest.h"
 #include "Level.h"
-#include "Named.h"
 #include "ObjectFactory.h"
 #include "PropertyFetcher.h"
 #include "RuntimeInfo.h"
@@ -308,9 +307,9 @@
    private:
     status_t getCombinedFrameworkMatrix(const std::shared_ptr<const HalManifest>& deviceManifest,
                                         CompatibilityMatrix* out, std::string* error = nullptr);
-    status_t getAllFrameworkMatrixLevels(std::vector<Named<CompatibilityMatrix>>* out,
+    status_t getAllFrameworkMatrixLevels(std::vector<CompatibilityMatrix>* out,
                                          std::string* error = nullptr);
-    status_t getOneMatrix(const std::string& path, Named<CompatibilityMatrix>* out,
+    status_t getOneMatrix(const std::string& path, CompatibilityMatrix* out,
                           std::string* error = nullptr);
     status_t addDirectoryManifests(const std::string& directory, HalManifest* manifests,
                                    std::string* error = nullptr);
diff --git a/include/vintf/WithFileName.h b/include/vintf/WithFileName.h
new file mode 100644
index 0000000..beed3e2
--- /dev/null
+++ b/include/vintf/WithFileName.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace android::vintf {
+
+// WithFileName is a class that attaches a name to an object.
+// The name is often the file name or path of the source file where the object
+// is deserialized from. The name is used for informational and debugging
+// purposes. It is not retained when the object is serialized.
+// It is not meant to be used as an identifier of the object:
+// - there may be duplicated names;
+// - name may be empty.
+struct WithFileName {
+    WithFileName() = default;
+    WithFileName(const std::string& fileName) : mFileName(fileName) {}
+    const std::string& fileName() const { return mFileName; }
+    void setFileName(std::string&& fileName) { mFileName = std::move(fileName); }
+    void setFileName(const std::string& fileName) { mFileName = fileName; }
+
+   private:
+    std::string mFileName;
+};
+
+}  // namespace android::vintf
diff --git a/parse_xml.cpp b/parse_xml.cpp
index e42f9f7..704ee8d 100644
--- a/parse_xml.cpp
+++ b/parse_xml.cpp
@@ -464,10 +464,7 @@
             !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.";
+        if (!object->isValid(error)) {
             return false;
         }
         return true;
@@ -760,7 +757,7 @@
                                   object->format);
             } break;
         }
-        if (!object->transportArch.isValid()) return false;
+        if (!object->transportArch.isValid(error)) return false;
 
         object->interfaces.clear();
         for (auto &&interface : interfaces) {
@@ -772,8 +769,8 @@
                 return false;
             }
         }
-        if (!object->isValid()) {
-            *error = "'" + object->name + "' is not a valid Manifest HAL.";
+        if (!object->isValid(error)) {
+            error->insert(0, "'" + object->name + "' is not a valid Manifest HAL: ");
             return false;
         }
 // Do not check for target-side libvintf to avoid restricting upgrade accidentally.
@@ -1033,11 +1030,18 @@
             return false;
         }
 
-        std::vector<ManifestHal> hals;
-        if (!parseAttr(root, "type", &object->mType, error) ||
-            !parseChildren(root, manifestHalConverter, &hals, error)) {
+        if (!parseAttr(root, "type", &object->mType, error)) {
             return false;
         }
+
+        std::vector<ManifestHal> hals;
+        if (!parseChildren(root, manifestHalConverter, &hals, error)) {
+            return false;
+        }
+        for (auto&& hal : hals) {
+            hal.setFileName(object->fileName());
+        }
+
         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
diff --git a/test/AssembleVintfTest.cpp b/test/AssembleVintfTest.cpp
index 584bb48..75c32f7 100644
--- a/test/AssembleVintfTest.cpp
+++ b/test/AssembleVintfTest.cpp
@@ -235,7 +235,7 @@
     getInstance()->setFakeEnv("PRODUCT_ENFORCE_VINTF_MANIFEST", "true");
 
     resetOutput();
-    getInstance()->setCheckInputStream(makeStream(manifest(1)));
+    getInstance()->setCheckInputStream("check.xml", makeStream(manifest(1)));
     EXPECT_TRUE(getInstance()->assemble());
     EXPECT_IN(
         "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"1\">\n"
@@ -253,7 +253,7 @@
         getOutput());
 
     resetOutput();
-    getInstance()->setCheckInputStream(makeStream(manifest(2)));
+    getInstance()->setCheckInputStream("check.xml", makeStream(manifest(2)));
     EXPECT_TRUE(getInstance()->assemble());
     EXPECT_IN(
         "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"2\">\n"
@@ -271,7 +271,7 @@
         getOutput());
 
     resetOutput();
-    getInstance()->setCheckInputStream(makeStream(manifest(3)));
+    getInstance()->setCheckInputStream("check.xml", makeStream(manifest(3)));
     EXPECT_TRUE(getInstance()->assemble());
     EXPECT_IN(
         "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"3\">\n"
@@ -326,7 +326,7 @@
     getInstance()->setFakeEnv("PROVIDED_VNDK_VERSIONS", "P 26 27 ");
 
     std::string matrix = "<compatibility-matrix " + kMetaVersionStr + " type=\"device\"/>\n";
-    getInstance()->setCheckInputStream(makeStream(matrix));
+    getInstance()->setCheckInputStream("check.xml", makeStream(matrix));
     EXPECT_TRUE(getInstance()->assemble());
 }
 
@@ -339,7 +339,7 @@
         "        <version>O</version>\n"
         "    </vendor-ndk>\n"
         "</compatibility-matrix>\n";
-    getInstance()->setCheckInputStream(makeStream(matrix));
+    getInstance()->setCheckInputStream("check.xml", makeStream(matrix));
     EXPECT_FALSE(getInstance()->assemble());
 }
 
@@ -352,7 +352,7 @@
         "        <version>27</version>\n"
         "    </vendor-ndk>\n"
         "</compatibility-matrix>\n";
-    getInstance()->setCheckInputStream(makeStream(matrix));
+    getInstance()->setCheckInputStream("check.xml", makeStream(matrix));
     EXPECT_TRUE(getInstance()->assemble());
 }
 
@@ -408,7 +408,7 @@
                  {"PLATFORM_SEPOLICY_COMPAT_VERSIONS", "26.0 27.0"},
                  {"FRAMEWORK_VBMETA_VERSION", "1.0"},
                  {"PRODUCT_ENFORCE_VINTF_MANIFEST", "true"}});
-    getInstance()->setCheckInputStream(makeStream(gEmptyOutManifest));
+    getInstance()->setCheckInputStream("check.xml", makeStream(gEmptyOutManifest));
 
     addInput("compatibility_matrix.empty.xml",
              "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\">\n"
@@ -452,7 +452,7 @@
                  {"PLATFORM_SEPOLICY_COMPAT_VERSIONS", "26.0 27.0"},
                  {"FRAMEWORK_VBMETA_VERSION", "1.0"},
                  {"PRODUCT_ENFORCE_VINTF_MANIFEST", "true"}});
-    getInstance()->setCheckInputStream(makeStream(gEmptyOutManifest));
+    getInstance()->setCheckInputStream("check.xml", makeStream(gEmptyOutManifest));
 
     addInput("compatibility_matrix.empty.xml",
              "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\">\n"
@@ -475,7 +475,7 @@
                  {"PLATFORM_SEPOLICY_COMPAT_VERSIONS", "26.0 27.0"},
                  {"FRAMEWORK_VBMETA_VERSION", "1.0"},
                  {"PRODUCT_ENFORCE_VINTF_MANIFEST", "true"}});
-    getInstance()->setCheckInputStream(makeStream(gEmptyOutManifest));
+    getInstance()->setCheckInputStream("check.xml", makeStream(gEmptyOutManifest));
 
     addInput("compatibility_matrix.foobar.xml",
              "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\">\n"
@@ -656,7 +656,7 @@
         "        </config>\n"
         "    </kernel>\n"
         "</compatibility-matrix>\n");
-    getInstance()->setCheckInputStream(makeStream(
+    getInstance()->setCheckInputStream("check.xml", makeStream(
         "<manifest " + kMetaVersionStr + " type=\"device\" target-level=\"1\">\n"
         "    <kernel target-level=\"1\" version=\"3.18.0\"/>\n"
         "    <sepolicy>\n"
@@ -675,7 +675,7 @@
         "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"1\">\n"
         "    <kernel version=\"3.18.0\" level=\"1\"/>\n"
         "</compatibility-matrix>\n");
-    getInstance()->setCheckInputStream(makeStream(
+    getInstance()->setCheckInputStream("check.xml", makeStream(
         "<manifest " + kMetaVersionStr + " type=\"device\" target-level=\"1\">\n"
         "    <kernel target-level=\"1\"/>\n"
         "    <sepolicy>\n"
diff --git a/test/LibVintfTest.cpp b/test/LibVintfTest.cpp
index fac2306..6d56ba6 100644
--- a/test/LibVintfTest.cpp
+++ b/test/LibVintfTest.cpp
@@ -3242,13 +3242,6 @@
             "    </hal>\n"
             "</manifest>\n";
         ASSERT_TRUE(gHalManifestConverter(&manifest, xml, &error)) << error;
-        EXPECT_IN(
-            "android.hardware.foo:\n"
-            "    required: @1.1::IFoo/custom\n"
-            "    provided: \n"
-            "        @1.0::IFoo/custom\n"
-            "        @1.0::IFoo/default",
-            error);
     }
 }
 
@@ -3547,6 +3540,39 @@
     EXPECT_EQ(xml2, gHalManifestConverter(manifest1));
 }
 
+TEST_F(LibVintfTest, ManifestAddAllConflictMajorVersion) {
+    std::string head =
+            "<manifest " + kMetaVersionStr + " type=\"device\">\n"
+            "    <hal format=\"hidl\">\n"
+            "        <name>android.hardware.foo</name>\n"
+            "        <transport>hwbinder</transport>\n"
+            "        <version>";
+    std::string tail =
+            "</version>\n"
+            "        <interface>\n"
+            "            <name>IFoo</name>\n"
+            "        </interface>\n"
+            "    </hal>\n"
+            "</manifest>\n";
+
+    std::string xml1 = head + "1.0" + tail;
+    std::string xml2 = head + "1.1" + tail;
+
+    std::string error;
+    HalManifest manifest1;
+    manifest1.setFileName("1.xml");
+    ASSERT_TRUE(gHalManifestConverter(&manifest1, xml1, &error)) << error;
+    HalManifest manifest2;
+    manifest2.setFileName("2.xml");
+    ASSERT_TRUE(gHalManifestConverter(&manifest2, xml2, &error)) << error;
+
+    ASSERT_FALSE(manifest1.addAll(&manifest2, &error));
+
+    EXPECT_IN("android.hardware.foo", error);
+    EXPECT_IN("1.0 (from 1.xml)", error);
+    EXPECT_IN("1.1 (from 2.xml)", error);
+}
+
 TEST_F(LibVintfTest, ManifestAddAllConflictLevel) {
     std::string xml1 = "<manifest " + kMetaVersionStr + " type=\"device\" target-level=\"2\" />\n";
     std::string xml2 = "<manifest " + kMetaVersionStr + " type=\"device\" target-level=\"3\" />\n";
@@ -3945,33 +3971,32 @@
 
 struct FrameworkCompatibilityMatrixCombineTest : public LibVintfTest {
     virtual void SetUp() override {
-        matrices = {
-            {"compatibility_matrix.1_1.xml", CompatibilityMatrix{}},
-            {"compatibility_matrix.1_2.xml", CompatibilityMatrix{}},
-        };
+        matrices.resize(2);
+        matrices[0].setFileName("compatibility_matrix.1_1.xml");
+        matrices[1].setFileName("compatibility_matrix.1_2.xml");
     }
     // Access to private methods.
     std::unique_ptr<CompatibilityMatrix> combine(Level deviceLevel,
-                                                 std::vector<Named<CompatibilityMatrix>>* matrices,
+                                                 std::vector<CompatibilityMatrix>* matrices,
                                                  std::string* error) {
         return CompatibilityMatrix::combine(deviceLevel, matrices, error);
     }
 
-    std::vector<Named<CompatibilityMatrix>> matrices;
+    std::vector<CompatibilityMatrix> matrices;
     std::string error;
 };
 
 // Combining framework compatibility matrix with conflicting minlts fails
 TEST_F(FrameworkCompatibilityMatrixCombineTest, ConflictMinlts) {
     ASSERT_TRUE(gCompatibilityMatrixConverter(
-        &matrices[0].object,
+        &matrices[0],
         "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"1\">\n"
         "    <kernel version=\"3.18.5\" />\n"
         "</compatibility-matrix>\n",
         &error))
         << error;
     ASSERT_TRUE(gCompatibilityMatrixConverter(
-        &matrices[1].object,
+        &matrices[1],
         "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"1\">\n"
         "    <kernel version=\"3.18.6\" />\n"
         "</compatibility-matrix>\n",
@@ -4007,14 +4032,14 @@
         "    </kernel>\n";
 
     ASSERT_TRUE(gCompatibilityMatrixConverter(
-        &matrices[0].object,
+        &matrices[0],
         "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"1\">\n"
         "    <kernel version=\"3.18.5\" />\n" +
             conditionedKernel + "</compatibility-matrix>\n",
         &error))
         << error;
     ASSERT_TRUE(gCompatibilityMatrixConverter(
-        &matrices[1].object,
+        &matrices[1],
         "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"1\">\n" + simpleKernel +
             "</compatibility-matrix>\n",
         &error))
@@ -4031,7 +4056,7 @@
 // Combining framework compatibility matrix with conflicting sepolicy fails
 TEST_F(FrameworkCompatibilityMatrixCombineTest, ConflictSepolicy) {
     ASSERT_TRUE(gCompatibilityMatrixConverter(
-        &matrices[0].object,
+        &matrices[0],
         "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"1\">\n"
         "    <sepolicy>\n"
         "        <kernel-sepolicy-version>30</kernel-sepolicy-version>\n"
@@ -4040,7 +4065,7 @@
         &error))
         << error;
     ASSERT_TRUE(gCompatibilityMatrixConverter(
-        &matrices[1].object,
+        &matrices[1],
         "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"1\">\n"
         "    <sepolicy>\n"
         "        <kernel-sepolicy-version>29</kernel-sepolicy-version>\n"
@@ -4057,7 +4082,7 @@
 // Combining framework compatibility matrix with conflicting avb fails
 TEST_F(FrameworkCompatibilityMatrixCombineTest, ConflictAvb) {
     ASSERT_TRUE(gCompatibilityMatrixConverter(
-        &matrices[0].object,
+        &matrices[0],
         "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"1\">\n"
         "    <avb>\n"
         "        <vbmeta-version>1.1</vbmeta-version>\n"
@@ -4066,7 +4091,7 @@
         &error))
         << error;
     ASSERT_TRUE(gCompatibilityMatrixConverter(
-        &matrices[1].object,
+        &matrices[1],
         "<compatibility-matrix " + kMetaVersionStr + " type=\"framework\" level=\"1\">\n"
         "    <avb>\n"
         "        <vbmeta-version>1.0</vbmeta-version>\n"
@@ -4105,9 +4130,9 @@
     std::string hidlOptional = std::string(hidl).replace(hidl.find("false"), 5, "true");
     std::string error;
     {
-        ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[0].object, head1 + aidl + tail, &error))
+        ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[0], head1 + aidl + tail, &error))
             << error;
-        ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[1].object, head1 + hidl + tail, &error))
+        ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[1], head1 + hidl + tail, &error))
             << error;
 
         auto combined = combine(Level{1}, &matrices, &error);
@@ -4118,9 +4143,9 @@
         EXPECT_IN(hidl, combinedXml);
     }
     {
-        ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[0].object, head1 + aidl + tail, &error))
+        ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[0], head1 + aidl + tail, &error))
             << error;
-        ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[1].object, head2 + hidl + tail, &error))
+        ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[1], head2 + hidl + tail, &error))
             << error;
 
         auto combined = combine(Level{1}, &matrices, &error);
@@ -4131,9 +4156,9 @@
         EXPECT_IN(hidlOptional, combinedXml);
     }
     {
-        ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[0].object, head2 + aidl + tail, &error))
+        ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[0], head2 + aidl + tail, &error))
             << error;
-        ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[1].object, head1 + hidl + tail, &error))
+        ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[1], head1 + hidl + tail, &error))
             << error;
 
         auto combined = combine(Level{1}, &matrices, &error);
@@ -4147,18 +4172,17 @@
 
 struct DeviceCompatibilityMatrixCombineTest : public LibVintfTest {
     virtual void SetUp() override {
-        matrices = {
-            {"compatibility_matrix.1.xml", CompatibilityMatrix{}},
-            {"compatibility_matrix.2.xml", CompatibilityMatrix{}},
-        };
+        matrices.resize(2);
+        matrices[0].setFileName("compatibility_matrix.1.xml");
+        matrices[1].setFileName("compatibility_matrix.2.xml");
     }
     // Access to private methods.
-    std::unique_ptr<CompatibilityMatrix> combine(std::vector<Named<CompatibilityMatrix>>* matrices,
+    std::unique_ptr<CompatibilityMatrix> combine(std::vector<CompatibilityMatrix>* matrices,
                                                  std::string* error) {
         return CompatibilityMatrix::combineDeviceMatrices(matrices, error);
     }
 
-    std::vector<Named<CompatibilityMatrix>> matrices;
+    std::vector<CompatibilityMatrix> matrices;
     std::string error;
 };
 
@@ -4183,9 +4207,9 @@
         "            <instance>default</instance>\n"
         "        </interface>\n"
         "    </hal>\n"};
-    ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[0].object, head + halFoo + tail, &error))
+    ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[0], head + halFoo + tail, &error))
         << error;
-    ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[1].object, head + halBar + tail, &error))
+    ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[1], head + halBar + tail, &error))
         << error;
 
     auto combined = combine(&matrices, &error);
@@ -4209,8 +4233,8 @@
         "        <version>Q</version>\n"
         "    </vendor-ndk>\n"
         "</compatibility-matrix>\n"};
-    ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[0].object, vendorNdkP, &error)) << error;
-    ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[1].object, vendorNdkQ, &error)) << error;
+    ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[0], vendorNdkP, &error)) << error;
+    ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[1], vendorNdkQ, &error)) << error;
 
     auto combined = combine(&matrices, &error);
     ASSERT_EQ(nullptr, combined) << gCompatibilityMatrixConverter(*combined);
@@ -4237,9 +4261,9 @@
         "            <instance>default</instance>\n"
         "        </interface>\n"
         "    </hal>\n";
-    ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[0].object, head + aidl + tail, &error))
+    ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[0], head + aidl + tail, &error))
         << error;
-    ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[1].object, head + hidl + tail, &error))
+    ASSERT_TRUE(gCompatibilityMatrixConverter(&matrices[1], head + hidl + tail, &error))
         << error;
 
     auto combined = combine(&matrices, &error);
diff --git a/utils.h b/utils.h
index 5378b67..83cb874 100644
--- a/utils.h
+++ b/utils.h
@@ -33,6 +33,12 @@
 template <typename T>
 status_t fetchAllInformation(const FileSystem* fileSystem, const std::string& path,
                              const XmlConverter<T>& converter, T* outObject, std::string* error) {
+    if (outObject->fileName().empty()) {
+        outObject->setFileName(path);
+    } else {
+        outObject->setFileName(outObject->fileName() + ":" + path);
+    }
+
     std::string info;
     status_t result = fileSystem->fetch(path, &info, error);
 
diff --git a/xsd/compatibilityMatrix/Android.bp b/xsd/compatibilityMatrix/Android.bp
index 2df3460..1741495 100644
--- a/xsd/compatibilityMatrix/Android.bp
+++ b/xsd/compatibilityMatrix/Android.bp
@@ -14,8 +14,13 @@
 // limitations under the License.
 //
 
+filegroup {
+    name: "compatibility_matrix_schema",
+    srcs: ["compatibility_matrix.xsd"],
+}
+
 xsd_config {
     name: "compatibility_matrix",
-    srcs: ["compatibility_matrix.xsd"],
+    srcs: [":compatibility_matrix_schema"],
     package_name: "compatibility.matrix",
 }
diff --git a/xsd/compatibilityMatrix/api/current.txt b/xsd/compatibilityMatrix/api/current.txt
index a326dd1..f3a255a 100644
--- a/xsd/compatibilityMatrix/api/current.txt
+++ b/xsd/compatibilityMatrix/api/current.txt
@@ -71,8 +71,10 @@
     ctor public Kernel();
     method public compatibility.matrix.Kernel.Conditions getConditions();
     method public java.util.List<compatibility.matrix.Config> getConfig();
+    method public String getLevel();
     method public String getVersion();
     method public void setConditions(compatibility.matrix.Kernel.Conditions);
+    method public void setLevel(String);
     method public void setVersion(String);
   }
 
diff --git a/xsd/compatibilityMatrix/compatibility_matrix.xsd b/xsd/compatibilityMatrix/compatibility_matrix.xsd
index b8c9fa5..8ccb6b2 100644
--- a/xsd/compatibilityMatrix/compatibility_matrix.xsd
+++ b/xsd/compatibilityMatrix/compatibility_matrix.xsd
@@ -64,6 +64,7 @@
             <xs:element name="config" type="config" minOccurs="0" maxOccurs="unbounded"/>
         </xs:sequence>
         <xs:attribute name="version" type="xs:string"/>
+        <xs:attribute name="level" type="xs:string"/>
     </xs:complexType>
     <xs:complexType name="config">
         <xs:sequence>