checkvintf: Add --check-one option.

checkvintf --check-one can check consistency of VINTF files within
a single partition. Currently, only system and vendor is supported
(because product / odm / system_ext VINTF files cannot be retrieved
independently).

Test: checkvintf --check-one /system:$OUT/system
Test: checkvintf --check-one /vendor:$OUT/vendor
Test: m check-vintf-all

Change-Id: Ibb396ee9b2292877c95e4ed8b636a9005d400475
diff --git a/check_vintf.cpp b/check_vintf.cpp
index ae57807..d4dc3bc 100644
--- a/check_vintf.cpp
+++ b/check_vintf.cpp
@@ -42,11 +42,15 @@
 using Dirmap = std::map<std::string, std::string>;
 
 enum Option : int {
-    DUMP_FILE_LIST = 1,
-    ROOTDIR,
+    // Modes
     HELP,
-    PROPERTY,
+    DUMP_FILE_LIST = 1,
     CHECK_COMPAT,
+    CHECK_ONE,
+
+    // Options
+    ROOTDIR,
+    PROPERTY,
     DIR_MAP,
     KERNEL,
 };
@@ -55,12 +59,13 @@
 
 class HostFileSystem : public details::FileSystemImpl {
    public:
-    HostFileSystem(const Dirmap& dirmap) : mDirMap(dirmap) {}
+    HostFileSystem(const Dirmap& dirmap, status_t missingError)
+        : mDirMap(dirmap), mMissingError(missingError) {}
     status_t fetch(const std::string& path, std::string* fetched,
                    std::string* error) const override {
         auto resolved = resolve(path, error);
         if (resolved.empty()) {
-            return UNKNOWN_ERROR;
+            return mMissingError;
         }
         status_t status = details::FileSystemImpl::fetch(resolved, fetched, error);
         LOG(INFO) << "Fetch '" << resolved << "': " << toString(status);
@@ -70,7 +75,7 @@
                        std::string* error) const override {
         auto resolved = resolve(path, error);
         if (resolved.empty()) {
-            return UNKNOWN_ERROR;
+            return mMissingError;
         }
         status_t status = details::FileSystemImpl::listFiles(resolved, out, error);
         LOG(INFO) << "List '" << resolved << "': " << toString(status);
@@ -93,12 +98,13 @@
         if (error) {
             *error = "Cannot resolve path " + path;
         } else {
-            LOG(ERROR) << "Cannot resolve path " << path;
+            LOG(mMissingError == NAME_NOT_FOUND ? INFO : ERROR) << "Cannot resolve path " << path;
         }
         return "";
     }
 
     Dirmap mDirMap;
+    status_t mMissingError;
 };
 
 class PresetPropertyFetcher : public PropertyFetcher {
@@ -221,11 +227,14 @@
     int optionIndex;
     Args ret;
     std::vector<struct option> longopts{
-        {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST},
-        {"rootdir", required_argument, &longOptFlag, ROOTDIR},
+        // Modes
         {"help", no_argument, &longOptFlag, HELP},
-        {"property", required_argument, &longOptFlag, PROPERTY},
+        {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST},
         {"check-compat", no_argument, &longOptFlag, CHECK_COMPAT},
+        {"check-one", no_argument, &longOptFlag, CHECK_ONE},
+        // Options
+        {"rootdir", required_argument, &longOptFlag, ROOTDIR},
+        {"property", required_argument, &longOptFlag, PROPERTY},
         {"dirmap", required_argument, &longOptFlag, DIR_MAP},
         {"kernel", required_argument, &longOptFlag, KERNEL},
         {0, 0, 0, 0}};
@@ -296,11 +305,15 @@
 int usage(const char* me) {
     LOG(ERROR)
         << me << ": check VINTF metadata." << std::endl
-        << "    Options:" << std::endl
+        << "    Modes:" << std::endl
         << "        --dump-file-list: Dump a list of directories / files on device" << std::endl
         << "                that is required to be used by --check-compat." << std::endl
         << "        -c, --check-compat: check compatibility for files under the root" << std::endl
         << "                directory specified by --root-dir." << std::endl
+        << "        --check-one: check consistency of VINTF metadata for a single partition."
+        << std::endl
+        << std::endl
+        << "    Options:" << std::endl
         << "        --rootdir=<dir>: specify root directory for all metadata. Same as " << std::endl
         << "                --dirmap /:<dir>" << std::endl
         << "        -D, --property <key>=<value>: specify sysprops." << std::endl
@@ -342,13 +355,61 @@
 
     auto vintfObject =
         VintfObject::Builder()
-            .setFileSystem(std::make_unique<HostFileSystem>(dirmap))
+            .setFileSystem(std::make_unique<HostFileSystem>(dirmap, UNKNOWN_ERROR))
             .setPropertyFetcher(std::move(hostPropertyFetcher))
             .setRuntimeInfoFactory(std::make_unique<StaticRuntimeInfoFactory>(runtimeInfo))
             .build();
     return vintfObject->checkCompatibility(error, flags);
 }
 
+int checkDirmaps(const Dirmap& dirmap) {
+    auto exitCode = EX_OK;
+    for (auto&& [prefix, mappedPath] : dirmap) {
+        auto vintfObject =
+            VintfObject::Builder()
+                .setFileSystem(std::make_unique<HostFileSystem>(dirmap, NAME_NOT_FOUND))
+                .setPropertyFetcher(std::make_unique<PropertyFetcherNoOp>())
+                .setRuntimeInfoFactory(std::make_unique<StaticRuntimeInfoFactory>(nullptr))
+                .build();
+
+        if (android::base::StartsWith(prefix, "/system")) {
+            LOG(INFO) << "Checking system manifest.";
+            auto manifest = vintfObject->getFrameworkHalManifest();
+            if (!manifest) {
+                LOG(ERROR) << "Cannot fetch system manifest.";
+                exitCode = EX_SOFTWARE;
+            }
+            LOG(INFO) << "Checking system matrix.";
+            auto matrix = vintfObject->getFrameworkCompatibilityMatrix();
+            if (!matrix) {
+                LOG(ERROR) << "Cannot fetch system matrix.";
+                exitCode = EX_SOFTWARE;
+            }
+            continue;
+        }
+
+        if (android::base::StartsWith(prefix, "/vendor")) {
+            LOG(INFO) << "Checking vendor manifest.";
+            auto manifest = vintfObject->getDeviceHalManifest();
+            if (!manifest) {
+                LOG(ERROR) << "Cannot fetch vendor manifest.";
+                exitCode = EX_SOFTWARE;
+            }
+            LOG(INFO) << "Checking vendor matrix.";
+            auto matrix = vintfObject->getDeviceCompatibilityMatrix();
+            if (!matrix) {
+                LOG(ERROR) << "Cannot fetch vendor matrix.";
+                exitCode = EX_SOFTWARE;
+            }
+            continue;
+        }
+
+        LOG(ERROR) << "--check-one does not work with --dirmap " << prefix;
+        exitCode = EX_SOFTWARE;
+    }
+    return exitCode;
+}
+
 }  // namespace details
 }  // namespace vintf
 }  // namespace android
@@ -359,7 +420,7 @@
     using namespace android::vintf;
     using namespace android::vintf::details;
     // legacy usage: check_vintf <manifest.xml> <matrix.xml>
-    if (argc == 3) {
+    if (argc == 3 && *argv[1] != '-' && *argv[2] != '-') {
         int ret = checkCompatibilityForFiles(argv[1], argv[2]);
         if (ret >= 0) return ret;
     }
@@ -377,6 +438,12 @@
         return 0;
     }
 
+    auto dirmap = getDirmap(iterateValues(args, DIR_MAP));
+
+    if (!iterateValues(args, CHECK_ONE).empty()) {
+        return checkDirmaps(dirmap);
+    }
+
     auto checkCompat = iterateValues(args, CHECK_COMPAT);
     if (checkCompat.empty()) {
         return usage(argv[0]);
@@ -392,7 +459,6 @@
     }
 
     auto properties = getProperties(iterateValues(args, PROPERTY));
-    auto dirmap = getDirmap(iterateValues(args, DIR_MAP));
 
     std::shared_ptr<StaticRuntimeInfo> runtimeInfo;
     auto kernelArgs = iterateValues(args, KERNEL);