Check unused HALs in check_vintf

The check is lost when deleting BUILT_ASSEMBLED_VENDOR_MANIFEST
(see I9791abc44). Re-add the check.

The check is enforced when product matrix or device system matrix is
detected, similar to the condition before I9791abc44.

Also, delete unused check in assemble_vintf when
VINTF_ENFORCE_UNUSED_HALS is set.

Test: builds
Bug: 121287858

Change-Id: Ifc5435e2ef1b4936a488827fe824126bdc50deae
Merged-In: Ifc5435e2ef1b4936a488827fe824126bdc50deae
diff --git a/Android.bp b/Android.bp
index a78a6a3..bfbd353 100644
--- a/Android.bp
+++ b/Android.bp
@@ -67,6 +67,7 @@
     local_include_dirs: ["include/vintf"],
 
     export_shared_lib_headers: [
+        "libbase",
         "libhidl-gen-utils",
     ],
 
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/VintfObject.cpp b/VintfObject.cpp
index 1f1c0f4..6d86682 100644
--- a/VintfObject.cpp
+++ b/VintfObject.cpp
@@ -23,6 +23,7 @@
 #include <mutex>
 
 #include <android-base/logging.h>
+#include <android-base/result.h>
 #include <android-base/strings.h>
 
 #include "CompatibilityMatrix.h"
@@ -805,6 +806,55 @@
     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 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() {
+    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);
+    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 d4dc3bc..17e32df 100644
--- a/check_vintf.cpp
+++ b/check_vintf.cpp
@@ -24,6 +24,7 @@
 #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 <utils/Errors.h>
 #include <vintf/KernelConfigParser.h>
@@ -345,8 +346,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 +360,25 @@
             .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();
+    }
+    if (*hasFcmExt) {
+        return vintfObject->checkUnusedHals();
+    }
+    LOG(INFO) << "Skip checking unused HALs.";
+    return {};
 }
 
 int checkDirmaps(const Dirmap& dirmap) {
@@ -469,23 +488,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/VintfObject.h b/include/vintf/VintfObject.h
index ea48676..30d31bb 100644
--- a/include/vintf/VintfObject.h
+++ b/include/vintf/VintfObject.h
@@ -20,6 +20,8 @@
 #include <memory>
 #include <optional>
 
+#include <android-base/result.h>
+
 #include "CheckFlags.h"
 #include "CompatibilityMatrix.h"
 #include "FileSystem.h"
@@ -184,6 +186,34 @@
      */
     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
+     *
+     * 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();
+
    private:
     std::unique_ptr<FileSystem> mFileSystem;
     std::unique_ptr<ObjectFactory<RuntimeInfo>> mRuntimeInfoFactory;
@@ -306,7 +336,6 @@
                                  std::string* error = nullptr);
     status_t fetchVendorHalManifest(HalManifest* out, std::string* error = nullptr);
     status_t fetchFrameworkHalManifest(HalManifest* out, std::string* error = nullptr);
-
     static bool IsHalDeprecated(const MatrixHal& oldMatrixHal,
                                 const CompatibilityMatrix& targetMatrix,
                                 const ListInstances& listInstances, std::string* error);