Create a way to easily generate a compatibility matrix.

Test: BOARD_SEPOLICY_VERS=10000.0 assemble_vintf -i
      device/.../.../manifest.xml -m
Test: assemble_vintf -i system/libhidl/manifest.xml -m

Bug: 33633372
Bug: 37321309

Change-Id: I9b786040cff70cfcb495078f4277b6aae1b56ba5
diff --git a/HalManifest.cpp b/HalManifest.cpp
index e8b7a96..6197ceb 100644
--- a/HalManifest.cpp
+++ b/HalManifest.cpp
@@ -259,6 +259,32 @@
     return true;
 }
 
+CompatibilityMatrix HalManifest::generateCompatibleMatrix() const {
+    CompatibilityMatrix matrix;
+
+    for (const ManifestHal &manifestHal : getHals()) {
+        MatrixHal matrixHal{
+            .format = manifestHal.format,
+            .name = manifestHal.name,
+            .optional = true,
+        };
+        for (const Version &manifestVersion : manifestHal.versions) {
+            matrixHal.versionRanges.push_back({manifestVersion.majorVer, manifestVersion.minorVer});
+        }
+        matrix.add(std::move(matrixHal));
+    }
+    if (mType == SchemaType::FRAMEWORK) {
+        matrix.mType = SchemaType::DEVICE;
+        // VNDK does not need to be added for compatibility
+    } else if (mType == SchemaType::DEVICE) {
+        matrix.mType = SchemaType::FRAMEWORK;
+        matrix.framework.mSepolicy = Sepolicy(0u /* kernelSepolicyVersion */,
+                {{device.mSepolicyVersion.majorVer, device.mSepolicyVersion.minorVer}});
+    }
+
+    return matrix;
+}
+
 status_t HalManifest::fetchAllInformation(const std::string &path) {
     std::ifstream in;
     in.open(path);
diff --git a/assemble_vintf.cpp b/assemble_vintf.cpp
index d3846a2..cfa9bf6 100644
--- a/assemble_vintf.cpp
+++ b/assemble_vintf.cpp
@@ -49,7 +49,9 @@
         return true;
     }
 
-    static bool assemble(std::basic_istream<char>& inFile, std::basic_ostream<char>& outFile) {
+    static bool assemble(std::basic_istream<char>& inFile,
+                         std::basic_ostream<char>& outFile,
+                         bool isMatrix) {
         std::stringstream ss;
         ss << inFile.rdbuf();
         std::string fileContent = ss.str();
@@ -67,8 +69,25 @@
             }
         }
 
-        std::string outFileContent = gHalManifestConverter(halManifest);
-        outFile << outFileContent;
+        if (isMatrix) {
+            CompatibilityMatrix mat = halManifest.generateCompatibleMatrix();
+            std::string error;
+            if (!halManifest.checkCompatibility(mat, &error)) {
+                std::cerr << "FATAL ERROR: cannot generate a compatible matrix: "
+                          << error << std::endl;
+            }
+            outFile << "<!-- \n"
+                       "    Autogenerated skeleton compatibility matrix. \n"
+                       "    Use with caution. Modify it to suit your needs.\n"
+                       "    All HALs are set to optional.\n"
+                       "    Many entries other than HALs are zero-filled and\n"
+                       "    require human attention. \n"
+                       "-->\n"
+                    << gCompatibilityMatrixConverter(mat);
+        } else {
+            outFile << gHalManifestConverter(halManifest);
+        }
+
         outFile.flush();
 
         return true;
@@ -82,17 +101,20 @@
     std::cerr <<
         "assemble_vintf -h\n"
         "               Display this help text.\n"
-        "assemble_vintf -i <input file> [-o <output file>]\n"
+        "assemble_vintf -i <input file> [-o <output file>] [-m]\n"
         "               Fill in build-time flags into the given manifest.\n"
-        "               If no designated output file, write to stdout.\n";
+        "               If no designated output file, write to stdout.\n"
+        "               If -m is set, a compatible compatibility matrix is\n"
+        "               generated instead.\n";
 }
 
 int main(int argc, char **argv) {
     std::ifstream inFile;
     std::ofstream outFile;
     std::ostream* outFileRef = &std::cout;
+    bool isMatrix = false;
     int res;
-    while((res = getopt(argc, argv, "hi:o:v:")) >= 0) {
+    while((res = getopt(argc, argv, "hi:o:m")) >= 0) {
         switch (res) {
             case 'i': {
                 inFile.open(optarg);
@@ -111,6 +133,10 @@
                 outFileRef = &outFile;
             } break;
 
+            case 'm': {
+                isMatrix = true;
+            } break;
+
             case 'h':
             default: {
                 help();
@@ -125,7 +151,7 @@
         return 1;
     }
 
-    return ::android::vintf::AssembleVintf::assemble(inFile, *outFileRef)
+    return ::android::vintf::AssembleVintf::assemble(inFile, *outFileRef, isMatrix)
             ? 0 : 1;
 }
 
diff --git a/include/vintf/HalManifest.h b/include/vintf/HalManifest.h
index 4af7a30..23fedd9 100644
--- a/include/vintf/HalManifest.h
+++ b/include/vintf/HalManifest.h
@@ -88,6 +88,9 @@
     //     - manifest.sepolicy.version match one of compat-mat.sepolicy.sepolicy-version
     bool checkCompatibility(const CompatibilityMatrix &mat, std::string *error = nullptr) const;
 
+    // Generate a compatibility matrix such that checkCompatibility will return true.
+    CompatibilityMatrix generateCompatibleMatrix() const;
+
     // Add an hal to this manifest so that a HalManifest can be constructed programatically.
     bool add(ManifestHal &&hal);