Clean up HalGroup::forEachInstance usage

Add a few functions to MatrixInstance to print
out human-readable strings so that analyze_matrix / vintf
can use them.

Bug: 140832836
Test: libvintf_test
Test: analyze_matrix --interfaces \
    --input hardware/interfaces/compatibility_matrices/compatibility_matrix.current.xml
Test: adb shell vintf

Change-Id: Ice13f299ba24808119f5536ecacc2777cfa870a2
diff --git a/MatrixInstance.cpp b/MatrixInstance.cpp
index fb1cb33..2e95ee3 100644
--- a/MatrixInstance.cpp
+++ b/MatrixInstance.cpp
@@ -19,6 +19,7 @@
 #include <utility>
 
 #include "Regex.h"
+#include "parse_string.h"
 
 namespace android {
 namespace vintf {
@@ -100,5 +101,32 @@
     return mIsRegex;
 }
 
+std::string MatrixInstance::interfaceDescription(Version replaceVersion) const {
+    switch (format()) {
+        case HalFormat::HIDL:
+            [[fallthrough]];
+        case HalFormat::NATIVE: {
+            return toFQNameString(package(), replaceVersion, interface());
+        } break;
+        case HalFormat::AIDL: {
+            return toAidlFqnameString(package(), interface());
+        } break;
+    }
+}
+
+std::string MatrixInstance::description(Version replaceVersion) const {
+    std::string instanceDescription = isRegex() ? regexPattern() : exactInstance();
+    switch (format()) {
+        case HalFormat::HIDL:
+            [[fallthrough]];
+        case HalFormat::NATIVE: {
+            return toFQNameString(package(), replaceVersion, interface(), instanceDescription);
+        } break;
+        case HalFormat::AIDL: {
+            return toAidlFqnameString(package(), interface(), instanceDescription);
+        } break;
+    }
+}
+
 }  // namespace vintf
 }  // namespace android
diff --git a/analyze_matrix/analyze_matrix.cpp b/analyze_matrix/analyze_matrix.cpp
index 49134ef..6de9dd7 100644
--- a/analyze_matrix/analyze_matrix.cpp
+++ b/analyze_matrix/analyze_matrix.cpp
@@ -50,18 +50,11 @@
     auto set = std::make_optional<std::set<std::string>>();
     mat.forEachInstance([&set](const auto& matrixInstance) {
         for (auto minorVer = matrixInstance.versionRange().minMinor;
-             minorVer <= matrixInstance.versionRange().maxMinor; ++minorVer) {
-            FqInstance fqInstance;
-            if (!fqInstance.setTo(matrixInstance.package(), matrixInstance.versionRange().majorVer,
-                                  minorVer, matrixInstance.interface())) {
-                LOG(ERROR) << "Matrix not valid; '" << matrixInstance.package() << "@"
-                           << matrixInstance.versionRange().majorVer << "." << minorVer
-                           << "::" << matrixInstance.interface() << "' is not a valid FQName.";
-                set = std::nullopt;
-                return false;  // break
-            }
-
-            set->insert(fqInstance.string());
+             minorVer >= matrixInstance.versionRange().minMinor &&
+             minorVer <= matrixInstance.versionRange().maxMinor;
+             ++minorVer) {
+            Version version{matrixInstance.versionRange().majorVer, minorVer};
+            set->insert(matrixInstance.interfaceDescription(version));
         }
         return true;  // continue
     });
diff --git a/include/vintf/HalGroup.h b/include/vintf/HalGroup.h
index c97e3b5..9c5cda3 100644
--- a/include/vintf/HalGroup.h
+++ b/include/vintf/HalGroup.h
@@ -216,6 +216,7 @@
     }
 
    private:
+    friend class AnalyzeMatrix;
     friend class VintfObject;
 };
 
diff --git a/include/vintf/MatrixInstance.h b/include/vintf/MatrixInstance.h
index 4f9d3d1..ee0c5c1 100644
--- a/include/vintf/MatrixInstance.h
+++ b/include/vintf/MatrixInstance.h
@@ -62,6 +62,18 @@
 
     bool isRegex() const;
 
+    // Return a human-readable description of the interface.
+    // Version is replaced by replaceVersion.
+    // e.g. android.hardware.foo@1.0::IFoo (HIDL),
+    //      android.hardware.foo.IFoo (AIDL)
+    std::string interfaceDescription(Version replaceVersion) const;
+
+    // Return a human-readable description of the instance.
+    // Version is replaced by replaceVersion.
+    // e.g. android.hardware.foo@1.0::IFoo/default (HIDL),
+    //      android.hardware.foo.IFoo/default (AIDL)
+    std::string description(Version replaceVersion) const;
+
    private:
     HalFormat mFormat = HalFormat::HIDL;
     FqInstance mFqInstance;
diff --git a/include/vintf/parse_string.h b/include/vintf/parse_string.h
index d3f132b..deac080 100644
--- a/include/vintf/parse_string.h
+++ b/include/vintf/parse_string.h
@@ -124,7 +124,7 @@
 std::string toFQNameString(const std::string& interface, const std::string& instance);
 
 std::string toAidlFqnameString(const std::string& package, const std::string& interface,
-                               const std::string& instance);
+                               const std::string& instance = "");
 
 } // namespace vintf
 } // namespace android
diff --git a/main.cpp b/main.cpp
index baac511..95bc042 100644
--- a/main.cpp
+++ b/main.cpp
@@ -189,8 +189,7 @@
 void insert(const HalManifest* manifest, Table* table, const RowMutator& mutate) {
     if (manifest == nullptr) return;
     manifest->forEachInstance([&](const auto& manifestInstance) {
-        std::string key = toFQNameString(manifestInstance.package(), manifestInstance.version(),
-                                         manifestInstance.interface(), manifestInstance.instance());
+        std::string key = manifestInstance.description();
         mutate(&(*table)[key]);
         return true;
     });
@@ -200,12 +199,11 @@
     if (matrix == nullptr) return;
     matrix->forEachInstance([&](const auto& matrixInstance) {
         for (auto minorVer = matrixInstance.versionRange().minMinor;
-             minorVer <= matrixInstance.versionRange().maxMinor; ++minorVer) {
-            std::string key = toFQNameString(
-                matrixInstance.package(), Version{matrixInstance.versionRange().majorVer, minorVer},
-                matrixInstance.interface(),
-                matrixInstance.isRegex() ? matrixInstance.regexPattern()
-                                         : matrixInstance.exactInstance());
+             minorVer >= matrixInstance.versionRange().minMinor &&
+             minorVer <= matrixInstance.versionRange().maxMinor;
+             ++minorVer) {
+            Version version{matrixInstance.versionRange().majorVer, minorVer};
+            std::string key = matrixInstance.description(version);
             auto it = table->find(key);
             if (it == table->end()) {
                 mutate(&(*table)[key]);
diff --git a/parse_string.cpp b/parse_string.cpp
index 9383017..869b82e 100644
--- a/parse_string.cpp
+++ b/parse_string.cpp
@@ -539,7 +539,12 @@
 
 std::string toAidlFqnameString(const std::string& package, const std::string& interface,
                                const std::string& instance) {
-    return package + "." + interface + "/" + instance;
+    std::stringstream ss;
+    ss << package << "." << interface;
+    if (!instance.empty()) {
+        ss << "/" << instance;
+    }
+    return ss.str();
 }
 
 } // namespace vintf
diff --git a/test/vintf_object_tests.cpp b/test/vintf_object_tests.cpp
index 70b2542..1081eb8 100644
--- a/test/vintf_object_tests.cpp
+++ b/test/vintf_object_tests.cpp
@@ -790,7 +790,7 @@
     FqInstance expectInstance;
     EXPECT_TRUE(expectInstance.setTo("android.hardware.foo@1.0::IFoo/default"));
     bool found = false;
-    fcm->forEachInstance([&found, &expectInstance](const auto& matrixInstance) {
+    fcm->forEachHidlInstance([&found, &expectInstance](const auto& matrixInstance) {
         found |= matrixInstance.isSatisfiedBy(expectInstance);
         return !found;  // continue if not found
     });