lshal: add --types=lazy|z

Add a new section to lshal that shows HALs in the manifest
  - as hwbinder HALs but not registered
  - as passthrough HALs but no impl found

Fixes: 71555570
Test: lshal_test
Test: lshal --types=z

Change-Id: I6947d9e7e08991fa2497f40c81005f885d408931
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 1a412b2..7760a09 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -313,6 +313,8 @@
             return &mImplementationsTable;
         case HalType::VINTF_MANIFEST:
             return &mManifestHalsTable;
+        case HalType::LAZY_HALS:
+            return &mLazyHalsTable;
         default:
             LOG(FATAL) << "Unknown HAL type " << static_cast<int64_t>(type);
             return nullptr;
@@ -387,6 +389,10 @@
             "These may return subclasses through their respective HIDL_FETCH_I* functions.");
     mManifestHalsTable.setDescription(
             "All HALs that are in VINTF manifest.");
+    mLazyHalsTable.setDescription(
+            "All HALs that are declared in VINTF manifest:\n"
+            "   - as hwbinder HALs but are not registered to hwservicemanager, and\n"
+            "   - as hwbinder/passthrough HALs with no implementation.");
 }
 
 bool ListCommand::addEntryWithInstance(const TableEntry& entry,
@@ -810,6 +816,63 @@
     return status;
 }
 
+Status ListCommand::fetchLazyHals() {
+    using vintf::operator<<;
+
+    if (!shouldFetchHalType(HalType::LAZY_HALS)) { return OK; }
+    Status status = OK;
+
+    for (const TableEntry& manifestEntry : mManifestHalsTable) {
+        if (manifestEntry.transport == vintf::Transport::HWBINDER) {
+            if (!hasHwbinderEntry(manifestEntry)) {
+                mLazyHalsTable.add(TableEntry(manifestEntry));
+            }
+            continue;
+        }
+        if (manifestEntry.transport == vintf::Transport::PASSTHROUGH) {
+            if (!hasPassthroughEntry(manifestEntry)) {
+                mLazyHalsTable.add(TableEntry(manifestEntry));
+            }
+            continue;
+        }
+        err() << "Warning: unrecognized transport in VINTF manifest: "
+              << manifestEntry.transport;
+        status |= VINTF_ERROR;
+    }
+    return status;
+}
+
+bool ListCommand::hasHwbinderEntry(const TableEntry& entry) const {
+    for (const TableEntry& existing : mServicesTable) {
+        if (existing.interfaceName == entry.interfaceName) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool ListCommand::hasPassthroughEntry(const TableEntry& entry) const {
+    FqInstance entryFqInstance;
+    if (!entryFqInstance.setTo(entry.interfaceName)) {
+        return false; // cannot parse, so add it anyway.
+    }
+    for (const TableEntry& existing : mImplementationsTable) {
+        FqInstance existingFqInstance;
+        if (!existingFqInstance.setTo(getPackageAndVersion(existing.interfaceName))) {
+            continue;
+        }
+
+        // For example, manifest may say graphics.mapper@2.1 but passthroughServiceManager
+        // can only list graphics.mapper@2.0.
+        if (entryFqInstance.getPackage() == existingFqInstance.getPackage() &&
+            vintf::Version{entryFqInstance.getVersion()}
+                .minorAtLeast(vintf::Version{existingFqInstance.getVersion()})) {
+            return true;
+        }
+    }
+    return false;
+}
+
 Status ListCommand::fetch() {
     Status status = OK;
     auto bManager = mLshal.serviceManager();
@@ -830,11 +893,24 @@
         status |= fetchAllLibraries(pManager);
     }
     status |= fetchManifestHals();
+    status |= fetchLazyHals();
     return status;
 }
 
 void ListCommand::initFetchTypes() {
+    // TODO: refactor to do polymorphism on each table (so that dependency graph is not hardcoded).
+    static const std::map<HalType, std::set<HalType>> kDependencyGraph{
+        {HalType::LAZY_HALS, {HalType::BINDERIZED_SERVICES,
+                              HalType::PASSTHROUGH_LIBRARIES,
+                              HalType::VINTF_MANIFEST}},
+    };
     mFetchTypes.insert(mListTypes.begin(), mListTypes.end());
+    for (HalType listType : mListTypes) {
+        auto it = kDependencyGraph.find(listType);
+        if (it != kDependencyGraph.end()) {
+            mFetchTypes.insert(it->second.begin(), it->second.end());
+        }
+    }
 }
 
 void ListCommand::registerAllOptions() {
@@ -951,6 +1027,8 @@
             {"l", HalType::PASSTHROUGH_LIBRARIES},
             {"vintf", HalType::VINTF_MANIFEST},
             {"v", HalType::VINTF_MANIFEST},
+            {"lazy", HalType::LAZY_HALS},
+            {"z", HalType::LAZY_HALS},
         };
 
         std::vector<std::string> halTypesArgs = split(std::string(arg), ',');