Expand virtio_block check to other virtual devices

The Android Emulator isn't the only virtual device the virtio-block
detection code is useful for, and those platforms might not set any
discriminating properties to indicate that they are virtual.

Rework the virtio-block major detection to use /proc/devices instead
of hardcoding the assumption that any virtual platform can have
virtio-block at any experimental major; the new code permits only the
exact experimental major assigned to virtio-block.

The new code runs everywhere, but it will only run once and could be
expanded later to detect dynamic or experimental majors.

Bug: 156286088
Change-Id: Ieae805d08fddd0124a397636f04d99194a9ef7e5
diff --git a/Utils.cpp b/Utils.cpp
index 1e20d75..b129990 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -77,6 +77,7 @@
 static const char* kBlkidPath = "/system/bin/blkid";
 static const char* kKeyPath = "/data/misc/vold";
 
+static const char* kProcDevices = "/proc/devices";
 static const char* kProcFilesystems = "/proc/filesystems";
 
 static const char* kAndroidDir = "/Android/";
@@ -1103,8 +1104,39 @@
     }
 }
 
-bool IsRunningInEmulator() {
-    return android::base::GetBoolProperty("ro.kernel.qemu", false);
+static unsigned int GetMajorBlockVirtioBlk() {
+    std::string devices;
+    if (!ReadFileToString(kProcDevices, &devices)) {
+        PLOG(ERROR) << "Unable to open /proc/devices";
+        return 0;
+    }
+
+    bool blockSection = false;
+    for (auto line : android::base::Split(devices, "\n")) {
+        if (line == "Block devices:") {
+            blockSection = true;
+        } else if (line == "Character devices:") {
+            blockSection = false;
+        } else if (blockSection) {
+            auto tokens = android::base::Split(line, " ");
+            if (tokens.size() == 2 && tokens[1] == "virtblk") {
+                return std::stoul(tokens[0]);
+            }
+        }
+    }
+
+    return 0;
+}
+
+bool IsVirtioBlkDevice(unsigned int major) {
+    // Most virtualized platforms expose block devices with the virtio-blk
+    // block device driver. Unfortunately, this driver does not use a fixed
+    // major number, but relies on the kernel to assign one from a specific
+    // range of block majors, which are allocated for "LOCAL/EXPERIMENAL USE"
+    // per Documentation/devices.txt. This is true even for the latest Linux
+    // kernel (4.4; see init() in drivers/block/virtio_blk.c).
+    static unsigned int kMajorBlockVirtioBlk = GetMajorBlockVirtioBlk();
+    return kMajorBlockVirtioBlk && major == kMajorBlockVirtioBlk;
 }
 
 static status_t findMountPointsWithPrefix(const std::string& prefix,