Merge "Add system_ext manifest"
diff --git a/Android.bp b/Android.bp
index a78a6a3..12ab161 100644
--- a/Android.bp
+++ b/Android.bp
@@ -63,13 +63,19 @@
         "libtinyxml2",
         "libz",
     ],
+    header_libs: [
+        "libhidlmetadata_headers",
+    ],
     export_include_dirs: ["include", "."],
     local_include_dirs: ["include/vintf"],
 
     export_shared_lib_headers: [
+        "libbase",
         "libhidl-gen-utils",
     ],
-
+    export_header_lib_headers: [
+        "libhidlmetadata_headers",
+    ],
     target: {
         host: {
             srcs: [
@@ -112,6 +118,7 @@
     static_libs: [
         "libbase",
         "libhidl-gen-utils",
+        "libhidlmetadata",
         "liblog",
         "libvintf",
         "libutils",
diff --git a/AssembleVintf.cpp b/AssembleVintf.cpp
index 1dec7ec..432945c 100644
--- a/AssembleVintf.cpp
+++ b/AssembleVintf.cpp
@@ -265,25 +265,6 @@
                 return false;
             }
         }
-
-        // Check HALs in device manifest that are not in framework matrix.
-        if (getBooleanFlag("VINTF_ENFORCE_NO_UNUSED_HALS")) {
-            auto unused = manifest.checkUnusedHals(matrix);
-            if (!unused.empty()) {
-                std::cerr << "Error: The following instances are in the device manifest but "
-                          << "not specified in framework compatibility matrix: " << std::endl
-                          << "    " << android::base::Join(unused, "\n    ") << std::endl
-                          << "Suggested fix:" << std::endl
-                          << "1. Check for any typos in device manifest or framework compatibility "
-                          << "matrices with FCM version >= " << matrix.level() << "." << std::endl
-                          << "2. Add them to any framework compatibility matrix with FCM "
-                          << "version >= " << matrix.level() << " where applicable." << std::endl
-                          << "3. Add them to DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE "
-                          << "or DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE." << std::endl;
-
-                return false;
-            }
-        }
         return true;
     }
 
diff --git a/HalManifest.cpp b/HalManifest.cpp
index bd0379a..9118773 100644
--- a/HalManifest.cpp
+++ b/HalManifest.cpp
@@ -227,15 +227,44 @@
     return ret;
 }
 
-std::set<std::string> HalManifest::checkUnusedHals(const CompatibilityMatrix& mat) const {
+std::set<std::string> HalManifest::checkUnusedHals(
+    const CompatibilityMatrix& mat, const std::vector<HidlInterfaceMetadata>& hidlMetadata) const {
+    std::multimap<std::string, std::string> childrenMap;
+    for (const auto& child : hidlMetadata) {
+        for (const auto& parent : child.inherited) {
+            childrenMap.emplace(parent, child.name);
+        }
+    }
+
     std::set<std::string> ret;
 
-    forEachInstance([&ret, &mat](const auto& manifestInstance) {
-        if (!mat.matchInstance(manifestInstance.format(), manifestInstance.package(),
-                               manifestInstance.version(), manifestInstance.interface(),
-                               manifestInstance.instance())) {
-            ret.insert(manifestInstance.description());
+    forEachInstance([&ret, &mat, &childrenMap](const auto& manifestInstance) {
+        if (mat.matchInstance(manifestInstance.format(), manifestInstance.package(),
+                              manifestInstance.version(), manifestInstance.interface(),
+                              manifestInstance.instance())) {
+            // manifestInstance exactly matches an instance in |mat|.
+            return true;
         }
+        // For HIDL instances, If foo@2.0 inherits from foo@1.0, manifest may contain both, but
+        // matrix may contain only 2.0 if 1.0 is considered deprecated. Hence, if manifestInstance
+        // is 1.0, check all its children in the matrix too.
+        // If there is at least one match, do not consider it unused.
+        if (manifestInstance.format() == HalFormat::HIDL) {
+            auto range =
+                childrenMap.equal_range(manifestInstance.getFqInstance().getFqName().string());
+            for (auto it = range.first; it != range.second; ++it) {
+                FQName fqName;
+                CHECK(fqName.setTo(it->second));
+                if (mat.matchInstance(manifestInstance.format(), fqName.package(),
+                                      fqName.getVersion(), fqName.name(),
+                                      manifestInstance.instance())) {
+                    return true;
+                }
+            }
+        }
+
+        // If no match is found, consider it unused.
+        ret.insert(manifestInstance.description());
         return true;
     });
 
diff --git a/VintfObject.cpp b/VintfObject.cpp
index f397ef3..a19d3e1 100644
--- a/VintfObject.cpp
+++ b/VintfObject.cpp
@@ -18,12 +18,15 @@
 
 #include <dirent.h>
 
+#include <algorithm>
 #include <functional>
 #include <memory>
 #include <mutex>
 
 #include <android-base/logging.h>
+#include <android-base/result.h>
 #include <android-base/strings.h>
+#include <hidl/metadata.h>
 
 #include "CompatibilityMatrix.h"
 #include "parse_string.h"
@@ -639,26 +642,27 @@
 
 bool VintfObject::IsHalDeprecated(const MatrixHal& oldMatrixHal,
                                   const CompatibilityMatrix& targetMatrix,
-                                  const ListInstances& listInstances, std::string* error) {
+                                  const ListInstances& listInstances,
+                                  const ChildrenMap& childrenMap, std::string* appendedError) {
     bool isDeprecated = false;
     oldMatrixHal.forEachInstance([&](const MatrixInstance& oldMatrixInstance) {
-        if (IsInstanceDeprecated(oldMatrixInstance, targetMatrix, listInstances, error)) {
+        if (IsInstanceDeprecated(oldMatrixInstance, targetMatrix, listInstances, childrenMap,
+                                 appendedError)) {
             isDeprecated = true;
         }
-        return !isDeprecated;  // continue if no deprecated instance is found.
+        return true;  // continue to check next instance
     });
     return isDeprecated;
 }
 
-// Let oldMatrixInstance = package@x.y-w::interface with instancePattern.
-// If any "servedInstance" in listInstances(package@x.y::interface) matches instancePattern, return
-// true iff:
-// 1. package@x.?::interface/servedInstance is not in targetMatrix; OR
-// 2. package@x.z::interface/servedInstance is in targetMatrix but
-//    servedInstance is not in listInstances(package@x.z::interface)
+// Let oldMatrixInstance = package@x.y-w::interface/instancePattern.
+// If any "@servedVersion::interface/servedInstance" in listInstances(package@x.y::interface)
+// matches instancePattern, return true iff for all child interfaces (from
+// GetListedInstanceInheritance), IsFqInstanceDeprecated returns false.
 bool VintfObject::IsInstanceDeprecated(const MatrixInstance& oldMatrixInstance,
                                        const CompatibilityMatrix& targetMatrix,
-                                       const ListInstances& listInstances, std::string* error) {
+                                       const ListInstances& listInstances,
+                                       const ChildrenMap& childrenMap, std::string* appendedError) {
     const std::string& package = oldMatrixInstance.package();
     const Version& version = oldMatrixInstance.versionRange().minVer();
     const std::string& interface = oldMatrixInstance.interface();
@@ -668,60 +672,152 @@
         instanceHint.push_back(oldMatrixInstance.exactInstance());
     }
 
+    std::vector<std::string> accumulatedErrors;
     auto list = listInstances(package, version, interface, instanceHint);
+
     for (const auto& pair : list) {
         const std::string& servedInstance = pair.first;
         Version servedVersion = pair.second;
+        std::string servedFqInstanceString =
+            toFQNameString(package, servedVersion, interface, servedInstance);
         if (!oldMatrixInstance.matchInstance(servedInstance)) {
+            // ignore unrelated instance
             continue;
         }
 
-        // Find any package@x.? in target matrix, and check if instance is in target matrix.
-        bool foundInstance = false;
-        Version targetMatrixMinVer;
-        targetMatrix.forEachHidlInstanceOfPackage(package, [&](const auto& targetMatrixInstance) {
-            if (targetMatrixInstance.versionRange().majorVer == version.majorVer &&
-                targetMatrixInstance.interface() == interface &&
-                targetMatrixInstance.matchInstance(servedInstance)) {
-                targetMatrixMinVer = targetMatrixInstance.versionRange().minVer();
-                foundInstance = true;
-            }
-            return !foundInstance;  // continue if not found
-        });
-        if (!foundInstance) {
-            if (error) {
-                *error = toFQNameString(package, servedVersion, interface, servedInstance) +
-                         " is deprecated in compatibility matrix at FCM Version " +
-                         to_string(targetMatrix.level()) + "; it should not be served.";
-            }
-            return true;
+        auto inheritance = GetListedInstanceInheritance(package, servedVersion, interface,
+                                                        servedInstance, listInstances, childrenMap);
+        if (!inheritance.has_value()) {
+            accumulatedErrors.push_back(inheritance.error().message());
+            continue;
         }
 
-        // Assuming that targetMatrix requires @x.u-v, require that at least @x.u is served.
-        bool targetVersionServed = false;
-        for (const auto& newPair :
-             listInstances(package, targetMatrixMinVer, interface, instanceHint)) {
-            if (newPair.first == servedInstance) {
-                targetVersionServed = true;
+        std::vector<std::string> errors;
+        for (const auto& fqInstance : *inheritance) {
+            auto result = IsFqInstanceDeprecated(targetMatrix, oldMatrixInstance.format(),
+                                                 fqInstance, listInstances);
+            if (result.ok()) {
+                errors.clear();
                 break;
             }
+            errors.push_back(result.error().message());
         }
 
-        if (!targetVersionServed) {
-            appendLine(error, toFQNameString(package, servedVersion, interface, servedInstance) +
-                                  " is deprecated; requires at least " +
-                                  to_string(targetMatrixMinVer));
+        if (errors.empty()) {
+            continue;
+        }
+        accumulatedErrors.insert(accumulatedErrors.end(), errors.begin(), errors.end());
+    }
+
+    if (accumulatedErrors.empty()) {
+        return false;
+    }
+    appendLine(appendedError, android::base::Join(accumulatedErrors, "\n"));
+    return true;
+}
+
+// Check if fqInstance is listed in |listInstances|.
+bool VintfObject::IsInstanceListed(const ListInstances& listInstances,
+                                   const FqInstance& fqInstance) {
+    auto list =
+        listInstances(fqInstance.getPackage(), fqInstance.getVersion(), fqInstance.getInterface(),
+                      {fqInstance.getInstance()} /* instanceHint*/);
+    return std::any_of(list.begin(), list.end(),
+                       [&](const auto& pair) { return pair.first == fqInstance.getInstance(); });
+}
+
+// Return a list of FqInstance, where each element:
+// - is listed in |listInstances|; AND
+// - is, or inherits from, package@version::interface/instance (as specified by |childrenMap|)
+android::base::Result<std::vector<FqInstance>> VintfObject::GetListedInstanceInheritance(
+    const std::string& package, const Version& version, const std::string& interface,
+    const std::string& instance, const ListInstances& listInstances,
+    const ChildrenMap& childrenMap) {
+    FqInstance fqInstance;
+    if (!fqInstance.setTo(package, version.majorVer, version.minorVer, interface, instance)) {
+        return android::base::Error() << toFQNameString(package, version, interface, instance)
+                                      << " is not a valid FqInstance";
+    }
+
+    if (!IsInstanceListed(listInstances, fqInstance)) {
+        return {};
+    }
+
+    const FQName& fqName = fqInstance.getFqName();
+
+    std::vector<FqInstance> ret;
+    ret.push_back(fqInstance);
+
+    auto childRange = childrenMap.equal_range(fqName.string());
+    for (auto it = childRange.first; it != childRange.second; ++it) {
+        const auto& childFqNameString = it->second;
+        FQName childFqName;
+        if (!childFqName.setTo(childFqNameString)) {
+            return android::base::Error() << "Cannot parse " << childFqNameString << " as FQName";
+        }
+        FqInstance childFqInstance;
+        if (!childFqInstance.setTo(childFqName, fqInstance.getInstance())) {
+            return android::base::Error() << "Cannot merge " << childFqName.string() << "/"
+                                          << fqInstance.getInstance() << " as FqInstance";
+            continue;
+        }
+        if (!IsInstanceListed(listInstances, childFqInstance)) {
+            continue;
+        }
+        ret.push_back(childFqInstance);
+    }
+    return ret;
+}
+
+// Check if |fqInstance| is in |targetMatrix|; essentially equal to
+// targetMatrix.matchInstance(fqInstance), but provides richer error message. In details:
+// 1. package@x.?::interface/servedInstance is not in targetMatrix; OR
+// 2. package@x.z::interface/servedInstance is in targetMatrix but
+//    servedInstance is not in listInstances(package@x.z::interface)
+android::base::Result<void> VintfObject::IsFqInstanceDeprecated(
+    const CompatibilityMatrix& targetMatrix, HalFormat format, const FqInstance& fqInstance,
+    const ListInstances& listInstances) {
+    // Find minimum package@x.? in target matrix, and check if instance is in target matrix.
+    bool foundInstance = false;
+    Version targetMatrixMinVer{SIZE_MAX, SIZE_MAX};
+    targetMatrix.forEachInstanceOfPackage(
+        format, fqInstance.getPackage(), [&](const auto& targetMatrixInstance) {
+            if (targetMatrixInstance.versionRange().majorVer == fqInstance.getMajorVersion() &&
+                targetMatrixInstance.interface() == fqInstance.getInterface() &&
+                targetMatrixInstance.matchInstance(fqInstance.getInstance())) {
+                targetMatrixMinVer =
+                    std::min(targetMatrixMinVer, targetMatrixInstance.versionRange().minVer());
+                foundInstance = true;
+            }
             return true;
+        });
+    if (!foundInstance) {
+        return android::base::Error()
+               << fqInstance.string() << " is deprecated in compatibility matrix at FCM Version "
+               << targetMatrix.level() << "; it should not be served.";
+    }
+
+    // Assuming that targetMatrix requires @x.u-v, require that at least @x.u is served.
+    bool targetVersionServed = false;
+    for (const auto& newPair :
+         listInstances(fqInstance.getPackage(), targetMatrixMinVer, fqInstance.getInterface(),
+                       {fqInstance.getInstance()} /* instanceHint */)) {
+        if (newPair.first == fqInstance.getInstance()) {
+            targetVersionServed = true;
+            break;
         }
     }
 
-    return false;
+    if (!targetVersionServed) {
+        return android::base::Error()
+               << fqInstance.string() << " is deprecated; requires at least " << targetMatrixMinVer;
+    }
+    return {};
 }
 
-int32_t VintfObject::CheckDeprecation(const ListInstances& listInstances, std::string* error) {
-    return GetInstance()->checkDeprecation(listInstances, error);
-}
-int32_t VintfObject::checkDeprecation(const ListInstances& listInstances, std::string* error) {
+int32_t VintfObject::checkDeprecation(const ListInstances& listInstances,
+                                      const std::vector<HidlInterfaceMetadata>& hidlMetadata,
+                                      std::string* error) {
     std::vector<Named<CompatibilityMatrix>> matrixFragments;
     auto matrixFragmentsStatus = getAllFrameworkMatrixLevels(&matrixFragments, error);
     if (matrixFragmentsStatus != OK) {
@@ -756,24 +852,33 @@
         return NAME_NOT_FOUND;
     }
 
-    bool hasDeprecatedHals = false;
+    std::multimap<std::string, std::string> childrenMap;
+    for (const auto& child : hidlMetadata) {
+        for (const auto& parent : child.inherited) {
+            childrenMap.emplace(parent, child.name);
+        }
+    }
+
+    // Find a list of possibly deprecated HALs by comparing |listInstances| with older matrices.
+    // 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;
 
         const auto& oldMatrix = namedMatrix.object;
         for (const MatrixHal& hal : oldMatrix.getHals()) {
-            hasDeprecatedHals |= IsHalDeprecated(hal, *targetMatrix, listInstances, error);
+            if (IsHalDeprecated(hal, *targetMatrix, listInstances, childrenMap, error)) {
+                isDeprecated = true;
+            }
         }
     }
 
-    return hasDeprecatedHals ? DEPRECATED : NO_DEPRECATED_HALS;
+    return isDeprecated ? DEPRECATED : NO_DEPRECATED_HALS;
 }
 
-int32_t VintfObject::CheckDeprecation(std::string* error) {
-    return GetInstance()->checkDeprecation(error);
-}
-int32_t VintfObject::checkDeprecation(std::string* error) {
+int32_t VintfObject::checkDeprecation(const std::vector<HidlInterfaceMetadata>& hidlMetadata,
+                                      std::string* error) {
     using namespace std::placeholders;
     auto deviceManifest = getDeviceHalManifest();
     ListInstances inManifest =
@@ -789,7 +894,7 @@
                 });
             return ret;
         };
-    return checkDeprecation(inManifest, error);
+    return checkDeprecation(inManifest, hidlMetadata, error);
 }
 
 Level VintfObject::getKernelLevel(std::string* error) {
@@ -817,6 +922,60 @@
     return mRuntimeInfoFactory;
 }
 
+android::base::Result<bool> VintfObject::hasFrameworkCompatibilityMatrixExtensions() {
+    std::vector<Named<CompatibilityMatrix>> matrixFragments;
+    std::string error;
+    status_t status = getAllFrameworkMatrixLevels(&matrixFragments, &error);
+    if (status != OK) {
+        return android::base::Error(-status)
+               << "Cannot get all framework matrix fragments: " << error;
+    }
+    for (const auto& namedMatrix : matrixFragments) {
+        // Returns true if product matrix exists.
+        if (android::base::StartsWith(namedMatrix.name, kProductVintfDir)) {
+            return true;
+        }
+        // Returns true if system_ext matrix exists.
+        if (android::base::StartsWith(namedMatrix.name, 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()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+android::base::Result<void> VintfObject::checkUnusedHals(
+    const std::vector<HidlInterfaceMetadata>& hidlMetadata) {
+    auto matrix = getFrameworkCompatibilityMatrix();
+    if (matrix == nullptr) {
+        return android::base::Error(-NAME_NOT_FOUND) << "Missing framework matrix.";
+    }
+    auto manifest = getDeviceHalManifest();
+    if (manifest == nullptr) {
+        return android::base::Error(-NAME_NOT_FOUND) << "Missing device manifest.";
+    }
+    auto unused = manifest->checkUnusedHals(*matrix, hidlMetadata);
+    if (!unused.empty()) {
+        return android::base::Error()
+               << "The following instances are in the device manifest but "
+               << "not specified in framework compatibility matrix: \n"
+               << "    " << android::base::Join(unused, "\n    ") << "\n"
+               << "Suggested fix:\n"
+               << "1. Check for any typos in device manifest or framework compatibility "
+               << "matrices with FCM version >= " << matrix->level() << ".\n"
+               << "2. Add them to any framework compatibility matrix with FCM "
+               << "version >= " << matrix->level() << " where applicable.\n"
+               << "3. Add them to DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE "
+               << "or DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE.";
+    }
+    return {};
+}
+
 // make_unique does not work because VintfObject constructor is private.
 VintfObject::Builder::Builder() : mObject(std::unique_ptr<VintfObject>(new VintfObject())) {}
 
diff --git a/assemble_vintf_main.cpp b/assemble_vintf_main.cpp
index b25a477..d74cc03 100644
--- a/assemble_vintf_main.cpp
+++ b/assemble_vintf_main.cpp
@@ -49,7 +49,6 @@
                  "               After writing the output file, the program checks against\n"
                  "               the \"check file\", depending on environment variables.\n"
                  "               - PRODUCT_ENFORCE_VINTF_MANIFEST=true: check compatibility\n"
-                 "               - VINTF_ENFORCE_NO_UNUSED_HALS  =true: check unused HALs\n"
                  "               If any check fails, an error message is written to stderr.\n"
                  "               Return 1.\n"
                  "    --kernel=<version>:<android-base.config>[:<android-base-arch.config>[...]]\n"
diff --git a/check_vintf.cpp b/check_vintf.cpp
index b5a4553..8ef1ca8 100644
--- a/check_vintf.cpp
+++ b/check_vintf.cpp
@@ -24,7 +24,9 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
+#include <android-base/result.h>
 #include <android-base/strings.h>
+#include <hidl/metadata.h>
 #include <utils/Errors.h>
 #include <vintf/KernelConfigParser.h>
 #include <vintf/VintfObject.h>
@@ -345,8 +347,8 @@
     return EX_USAGE;
 }
 
-int checkAllFiles(const Dirmap& dirmap, const Properties& props,
-                  std::shared_ptr<StaticRuntimeInfo> runtimeInfo, std::string* error) {
+android::base::Result<void> checkAllFiles(const Dirmap& dirmap, const Properties& props,
+                                          std::shared_ptr<StaticRuntimeInfo> runtimeInfo) {
     auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
     hostPropertyFetcher->setProperties(props);
 
@@ -359,7 +361,31 @@
             .setPropertyFetcher(std::move(hostPropertyFetcher))
             .setRuntimeInfoFactory(std::make_unique<StaticRuntimeInfoFactory>(runtimeInfo))
             .build();
-    return vintfObject->checkCompatibility(error, flags);
+
+    std::string error;
+    int compatibleResult = vintfObject->checkCompatibility(&error, flags);
+    if (compatibleResult == INCOMPATIBLE) {
+        return android::base::Error() << error;
+    }
+    if (compatibleResult != COMPATIBLE) {
+        return android::base::Error(-compatibleResult) << error;
+    }
+
+    auto hasFcmExt = vintfObject->hasFrameworkCompatibilityMatrixExtensions();
+    if (!hasFcmExt.has_value()) {
+        return hasFcmExt.error();
+    }
+    auto deviceManifest = vintfObject->getDeviceHalManifest();
+    if (deviceManifest == nullptr) {
+        return android::base::Error(-NAME_NOT_FOUND) << "No device HAL manifest";
+    }
+    auto targetFcm = deviceManifest->level();
+    if (*hasFcmExt || (targetFcm != Level::UNSPECIFIED && targetFcm >= Level::R)) {
+        auto hidlMetadata = HidlInterfaceMetadata::all();
+        return vintfObject->checkUnusedHals(hidlMetadata);
+    }
+    LOG(INFO) << "Skip checking unused HALs.";
+    return {};
 }
 
 int checkDirmaps(const Dirmap& dirmap, const Properties& props) {
@@ -470,23 +496,22 @@
         }
     }
 
-    std::string error;
     if (dirmap.empty()) {
         LOG(ERROR) << "Missing --rootdir or --dirmap option.";
         return usage(argv[0]);
     }
 
-    int compat = checkAllFiles(dirmap, properties, runtimeInfo, &error);
+    auto compat = checkAllFiles(dirmap, properties, runtimeInfo);
 
-    if (compat == COMPATIBLE) {
+    if (compat.ok()) {
         std::cout << "COMPATIBLE" << std::endl;
         return EX_OK;
     }
-    if (compat == INCOMPATIBLE) {
-        LOG(ERROR) << "files are incompatible: " << error;
+    if (compat.error().code() == 0) {
+        LOG(ERROR) << "files are incompatible: " << compat.error();
         std::cout << "INCOMPATIBLE" << std::endl;
         return EX_DATAERR;
     }
-    LOG(ERROR) << strerror(-compat) << ": " << error;
+    LOG(ERROR) << strerror(compat.error().code()) << ": " << compat.error();
     return EX_SOFTWARE;
 }
diff --git a/include/vintf/HalGroup.h b/include/vintf/HalGroup.h
index 9c5cda3..db87cec 100644
--- a/include/vintf/HalGroup.h
+++ b/include/vintf/HalGroup.h
@@ -101,10 +101,10 @@
         });
     }
 
-    bool forEachHidlInstanceOfPackage(const std::string& package,
-                                      const std::function<bool(const InstanceType&)>& func) const {
+    bool forEachInstanceOfPackage(HalFormat format, const std::string& package,
+                                  const std::function<bool(const InstanceType&)>& func) const {
         for (const auto* hal : getHals(package)) {
-            if (hal->format != HalFormat::HIDL) {
+            if (hal->format != format) {
                 continue;
             }
             if (!hal->forEachInstance(func)) {
@@ -113,6 +113,10 @@
         }
         return true;
     }
+    bool forEachHidlInstanceOfPackage(const std::string& package,
+                                      const std::function<bool(const InstanceType&)>& func) const {
+        return forEachInstanceOfPackage(HalFormat::HIDL, package, func);
+    }
 
    protected:
     // Apply func to all instances of package@expectVersion::*/*.
diff --git a/include/vintf/HalManifest.h b/include/vintf/HalManifest.h
index e7b2a0e..41298f0 100644
--- a/include/vintf/HalManifest.h
+++ b/include/vintf/HalManifest.h
@@ -24,6 +24,8 @@
 #include <string>
 #include <vector>
 
+#include <hidl/metadata.h>
+
 #include "CheckFlags.h"
 #include "FileSystem.h"
 #include "HalGroup.h"
@@ -175,7 +177,9 @@
     // required HAL.
     // That is, return empty list iff
     // (instance in manifest) => (instance in matrix).
-    std::set<std::string> checkUnusedHals(const CompatibilityMatrix& mat) const;
+    std::set<std::string> checkUnusedHals(
+        const CompatibilityMatrix& mat,
+        const std::vector<HidlInterfaceMetadata>& hidlMetadata) const;
 
     // Check that manifest has no entries.
     bool empty() const;
diff --git a/include/vintf/VintfObject.h b/include/vintf/VintfObject.h
index 4807e03..fa25669 100644
--- a/include/vintf/VintfObject.h
+++ b/include/vintf/VintfObject.h
@@ -17,8 +17,15 @@
 #ifndef ANDROID_VINTF_VINTF_OBJECT_H_
 #define ANDROID_VINTF_VINTF_OBJECT_H_
 
+#include <map>
 #include <memory>
 #include <optional>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <android-base/result.h>
+#include <hidl/metadata.h>
 
 #include "CheckFlags.h"
 #include "CompatibilityMatrix.h"
@@ -165,7 +172,9 @@
      *         > 0 if there is at least one deprecated HAL
      *         < 0 if any error (mount partition fails, illformed XML, etc.)
      */
-    int32_t checkDeprecation(const ListInstances& listInstances, std::string* error = nullptr);
+    int32_t checkDeprecation(const ListInstances& listInstances,
+                             const std::vector<HidlInterfaceMetadata>& hidlMetadata,
+                             std::string* error = nullptr);
 
     /**
      * Check deprecation on existing VINTF metadata. Use Device Manifest as the
@@ -175,7 +184,8 @@
      *         > 0 if there is at least one deprecated HAL
      *         < 0 if any error (mount partition fails, illformed XML, etc.)
      */
-    int32_t checkDeprecation(std::string* error = nullptr);
+    int32_t checkDeprecation(const std::vector<HidlInterfaceMetadata>& hidlMetadata,
+                             std::string* error = nullptr);
 
     /**
      * Return kernel FCM version.
@@ -184,6 +194,36 @@
      */
     Level getKernelLevel(std::string* error = nullptr);
 
+    /**
+     * Returns true if the framework compatibility matrix has extensions. In
+     * other words, returns true if any of the following exists on the device:
+     * - device framework compatibility matrix
+     * - product framework compatibility matrix
+     * - system_ext framework compatibility matrix
+     *
+     * Return result:
+     * - true if framework compatibility matrix has extensions
+     * - false if framework compatibility
+     *     matrix does not have extensions.
+     * - !result.has_value() if any error. Check
+     *     result.error() for detailed message.
+     */
+    android::base::Result<bool> hasFrameworkCompatibilityMatrixExtensions();
+
+    /**
+     * Check that there are no unused HALs in HAL manifests. Currently, only
+     * device manifest is checked against framework compatibility matrix.
+     *
+     * Return result:
+     * - result.ok() if no unused HALs
+     * - !result.ok() && result.error().code() == 0 if with unused HALs. Check
+     *     result.error() for detailed message.
+     * - !result.ok() && result.error().code() != 0 if any error. Check
+     *     result.error() for detailed message.
+     */
+    android::base::Result<void> checkUnusedHals(
+        const std::vector<HidlInterfaceMetadata>& hidlMetadata);
+
    private:
     std::unique_ptr<FileSystem> mFileSystem;
     std::unique_ptr<ObjectFactory<RuntimeInfo>> mRuntimeInfoFactory;
@@ -265,31 +305,6 @@
     static std::shared_ptr<const RuntimeInfo> GetRuntimeInfo(
         bool skipCache = false, RuntimeInfo::FetchFlags flags = RuntimeInfo::FetchFlag::ALL);
 
-    /**
-     * Check deprecation on framework matrices with a provided predicate.
-     *
-     * @param listInstances predicate that takes parameter in this format:
-     *        android.hardware.foo@1.0::IFoo
-     *        and returns {{"default", version}...} if HAL is in use, where version =
-     *        first version in interfaceChain where package + major version matches.
-     *
-     * @return = 0 if success (no deprecated HALs)
-     *         > 0 if there is at least one deprecated HAL
-     *         < 0 if any error (mount partition fails, illformed XML, etc.)
-     */
-    static int32_t CheckDeprecation(const ListInstances& listInstances,
-                                    std::string* error = nullptr);
-
-    /**
-     * Check deprecation on existing VINTF metadata. Use Device Manifest as the
-     * predicate to check if a HAL is in use.
-     *
-     * @return = 0 if success (no deprecated HALs)
-     *         > 0 if there is at least one deprecated HAL
-     *         < 0 if any error (mount partition fails, illformed XML, etc.)
-     */
-    static int32_t CheckDeprecation(std::string* error = nullptr);
-
    private:
     status_t getCombinedFrameworkMatrix(const std::shared_ptr<const HalManifest>& deviceManifest,
                                         CompatibilityMatrix* out, std::string* error = nullptr);
@@ -307,12 +322,24 @@
     status_t fetchVendorHalManifest(HalManifest* out, std::string* error = nullptr);
     status_t fetchFrameworkHalManifest(HalManifest* out, std::string* error = nullptr);
 
+    using ChildrenMap = std::multimap<std::string, std::string>;
     static bool IsHalDeprecated(const MatrixHal& oldMatrixHal,
                                 const CompatibilityMatrix& targetMatrix,
-                                const ListInstances& listInstances, std::string* error);
+                                const ListInstances& listInstances, const ChildrenMap& childrenMap,
+                                std::string* appendedError);
     static bool IsInstanceDeprecated(const MatrixInstance& oldMatrixInstance,
                                      const CompatibilityMatrix& targetMatrix,
-                                     const ListInstances& listInstances, std::string* error);
+                                     const ListInstances& listInstances,
+                                     const ChildrenMap& childrenMap, std::string* appendedError);
+
+    static android::base::Result<std::vector<FqInstance>> GetListedInstanceInheritance(
+        const std::string& package, const Version& version, const std::string& interface,
+        const std::string& instance, const ListInstances& listInstances,
+        const ChildrenMap& childrenMap);
+    static bool IsInstanceListed(const ListInstances& listInstances, const FqInstance& fqInstance);
+    static android::base::Result<void> IsFqInstanceDeprecated(
+        const CompatibilityMatrix& targetMatrix, HalFormat format, const FqInstance& fqInstance,
+        const ListInstances& listInstances);
 
    public:
     /**
diff --git a/main.cpp b/main.cpp
index 70433b9..5765f9b 100644
--- a/main.cpp
+++ b/main.cpp
@@ -321,8 +321,9 @@
     }
 
     if (vm && fcm) {
-        auto deprecate = VintfObject::CheckDeprecation(&error);
-        std::cout << "VintfObject::CheckDeprecation (against device manifest)? "
+        // TODO(b/131717099): Use correct information from libhidlmetadata
+        auto deprecate = VintfObject::GetInstance()->checkDeprecation({}, &error);
+        std::cout << "VintfObject::CheckDeprecation (against device manifest) (w/o hidlmetadata)? "
                   << deprecateString(deprecate);
         if (deprecate != NO_DEPRECATED_HALS) std::cout << ", " << error;
         std::cout << std::endl;
diff --git a/test/LibVintfTest.cpp b/test/LibVintfTest.cpp
index c22220b..fac2306 100644
--- a/test/LibVintfTest.cpp
+++ b/test/LibVintfTest.cpp
@@ -125,7 +125,7 @@
         return cm1->addAllXmlFilesAsOptional(cm2, e);
     }
     std::set<std::string> checkUnusedHals(const HalManifest& m, const CompatibilityMatrix& cm) {
-        return m.checkUnusedHals(cm);
+        return m.checkUnusedHals(cm, {});
     }
 
     std::map<std::string, HalInterface> testHalInterfaces() {
diff --git a/test/vintf_object_tests.cpp b/test/vintf_object_tests.cpp
index f7a0e36..14d7025 100644
--- a/test/vintf_object_tests.cpp
+++ b/test/vintf_object_tests.cpp
@@ -889,12 +889,12 @@
                 };
                 return ::android::OK;
             }));
-        expectFetch(kSystemVintfDir + "compatibility_matrix.1.xml", systemMatrixLevel1);
-        expectFetch(kSystemVintfDir + "compatibility_matrix.2.xml", systemMatrixLevel2);
+        expectFetchRepeatedly(kSystemVintfDir + "compatibility_matrix.1.xml", systemMatrixLevel1);
+        expectFetchRepeatedly(kSystemVintfDir + "compatibility_matrix.2.xml", systemMatrixLevel2);
         expectFileNotExist(StrEq(kProductMatrix));
         expectNeverFetch(kSystemLegacyMatrix);
 
-        expectFetch(kVendorManifest,
+        expectFetchRepeatedly(kVendorManifest,
                     "<manifest " + kMetaVersionStr + " type=\"device\" target-level=\"2\"/>");
         expectFileNotExist(StartsWith("/odm/"));
 
@@ -911,7 +911,7 @@
         "android.hardware.major@2.0::IMajor/default",
     });
     std::string error;
-    EXPECT_EQ(NO_DEPRECATED_HALS, vintfObject->checkDeprecation(pred, &error)) << error;
+    EXPECT_EQ(NO_DEPRECATED_HALS, vintfObject->checkDeprecation(pred, {}, &error)) << error;
 }
 
 TEST_F(DeprecateTest, CheckRemoved) {
@@ -921,7 +921,7 @@
         "android.hardware.major@2.0::IMajor/default",
     });
     std::string error;
-    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, &error))
+    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, {}, &error))
         << "removed@1.0 should be deprecated. " << error;
 }
 
@@ -931,7 +931,7 @@
         "android.hardware.major@2.0::IMajor/default",
     });
     std::string error;
-    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, &error))
+    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, {}, &error))
         << "minor@1.0 should be deprecated. " << error;
 }
 
@@ -942,7 +942,7 @@
         "android.hardware.major@2.0::IMajor/default",
     });
     std::string error;
-    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, &error))
+    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, {}, &error))
         << "minor@1.0::IMinor/legacy should be deprecated. " << error;
 }
 
@@ -953,7 +953,7 @@
         "android.hardware.major@2.0::IMajor/default",
     });
     std::string error;
-    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, &error))
+    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, {}, &error))
         << "minor@1.1::IMinor/legacy should be deprecated. " << error;
 }
 
@@ -964,7 +964,7 @@
         "android.hardware.major@2.0::IMajor/default",
     });
     std::string error;
-    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, &error))
+    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, {}, &error))
         << "major@1.0 should be deprecated. " << error;
 }
 
@@ -974,7 +974,36 @@
         "android.hardware.major@1.0::IMajor/default",
     });
     std::string error;
-    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, &error))
+    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, {}, &error))
+        << "major@1.0 should be deprecated. " << error;
+}
+
+TEST_F(DeprecateTest, HidlMetadataNotDeprecate) {
+    auto pred = getInstanceListFunc({
+        "android.hardware.major@1.0::IMajor/default",
+        "android.hardware.major@2.0::IMajor/default",
+    });
+    std::string error;
+    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, {}, &error))
+        << "major@1.0 should be deprecated. " << error;
+    std::vector<HidlInterfaceMetadata> hidlMetadata{
+      {"android.hardware.major@2.0::IMajor", {"android.hardware.major@1.0::IMajor"}},
+    };
+    EXPECT_EQ(NO_DEPRECATED_HALS, vintfObject->checkDeprecation(pred, hidlMetadata, &error))
+        << "major@1.0 should not be deprecated because it extends from 2.0: " << error;
+}
+
+TEST_F(DeprecateTest, HidlMetadataDeprecate) {
+    auto pred = getInstanceListFunc({
+        "android.hardware.major@1.0::IMajor/default",
+    });
+    std::string error;
+    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, {}, &error))
+        << "major@1.0 should be deprecated. " << error;
+    std::vector<HidlInterfaceMetadata> hidlMetadata{
+      {"android.hardware.major@2.0::IMajor", {"android.hardware.major@1.0::IMajor"}},
+    };
+    EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, hidlMetadata, &error))
         << "major@1.0 should be deprecated. " << error;
 }
 
@@ -1131,7 +1160,7 @@
         "android.hardware.regex@1.1::IRegex/regex_common/0",
         "android.hardware.regex@2.0::IRegex/default",
     });
-    EXPECT_EQ(NO_DEPRECATED_HALS, vintfObject->checkDeprecation(pred, &error)) << error;
+    EXPECT_EQ(NO_DEPRECATED_HALS, vintfObject->checkDeprecation(pred, {}, &error)) << error;
 
     for (const auto& deprecated : {
              "android.hardware.regex@1.0::IRegex/default",
@@ -1147,7 +1176,7 @@
             "android.hardware.regex@2.0::IRegex/default",
         });
         error.clear();
-        EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, &error))
+        EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, {}, &error))
             << deprecated << " should be deprecated. " << error;
     }
 }
@@ -1161,7 +1190,7 @@
         "android.hardware.regex@2.0::IRegex/regex/2.0/1",
         "android.hardware.regex@2.0::IRegex/default",
     });
-    EXPECT_EQ(NO_DEPRECATED_HALS, vintfObject->checkDeprecation(pred, &error)) << error;
+    EXPECT_EQ(NO_DEPRECATED_HALS, vintfObject->checkDeprecation(pred, {}, &error)) << error;
 
     for (const auto& deprecated : {
              "android.hardware.regex@1.0::IRegex/default",
@@ -1181,7 +1210,7 @@
         });
 
         error.clear();
-        EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, &error))
+        EXPECT_EQ(DEPRECATED, vintfObject->checkDeprecation(pred, {}, &error))
             << deprecated << " should be deprecated.";
     }
 }