assemble_vintf: Add checkUnusedHals

Test: m framework_compatibility_matrix.xml device_manifest.xml
Bug: 65028233

Change-Id: I466c60573676e8bf3a0d8d97000c380bdfec4b58
Merged-In: I466c60573676e8bf3a0d8d97000c380bdfec4b58
diff --git a/AssembleVintf.cpp b/AssembleVintf.cpp
index c478928..ef7c8ba 100644
--- a/AssembleVintf.cpp
+++ b/AssembleVintf.cpp
@@ -263,6 +263,37 @@
 
     std::basic_ostream<char>& out() const { return mOutRef == nullptr ? std::cout : *mOutRef; }
 
+    // If -c is provided, check it.
+    bool checkDualFile(const HalManifest& manifest, const CompatibilityMatrix& matrix) {
+        if (getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST")) {
+            std::string error;
+            if (!manifest.checkCompatibility(matrix, &error)) {
+                std::cerr << "Not compatible: " << error << std::endl;
+                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."
+                          << std::endl;
+
+                return false;
+            }
+        }
+        return true;
+    }
+
     template <typename S>
     using Schemas = std::vector<Named<S>>;
     using HalManifests = Schemas<HalManifest>;
@@ -342,8 +373,7 @@
                           << std::endl;
                 return false;
             }
-            if (!halManifest->checkCompatibility(checkMatrix, &error)) {
-                std::cerr << "Not compatible: " << error << std::endl;
+            if (!checkDualFile(*halManifest, checkMatrix)) {
                 return false;
             }
         }
@@ -513,9 +543,7 @@
         out() << gCompatibilityMatrixConverter(*matrix, mSerializeFlags);
         out().flush();
 
-        if (checkManifest != nullptr && getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST") &&
-            !checkManifest->checkCompatibility(*matrix, &error)) {
-            std::cerr << "Not compatible: " << error << std::endl;
+        if (checkManifest != nullptr && !checkDualFile(*checkManifest, *matrix)) {
             return false;
         }
 
diff --git a/test/AssembleVintfTest.cpp b/test/AssembleVintfTest.cpp
index b629c74..33acc1e 100644
--- a/test/AssembleVintfTest.cpp
+++ b/test/AssembleVintfTest.cpp
@@ -36,6 +36,8 @@
         auto s = makeStream("");
         mOutputStream = s.get();
         mInstance->setOutputStream(std::move(s));
+
+        getInstance()->setFakeEnv("PRODUCT_ENFORCE_VINTF_MANIFEST", "true");
     }
     virtual void TearDown() override { mInstance = nullptr; }