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,
diff --git a/Utils.h b/Utils.h
index 5e6ff1b..e04dcaa 100644
--- a/Utils.h
+++ b/Utils.h
@@ -155,8 +155,8 @@
 // TODO: promote to android::base
 bool Readlinkat(int dirfd, const std::string& path, std::string* result);
 
-/* Checks if Android is running in QEMU */
-bool IsRunningInEmulator();
+// Handles dynamic major assignment for virtio-block
+bool IsVirtioBlkDevice(unsigned int major);
 
 status_t UnmountTreeWithPrefix(const std::string& prefix);
 status_t UnmountTree(const std::string& mountPoint);
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index c0d0e77..f64f5f6 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -83,6 +83,7 @@
 using android::vold::DeleteDirContentsAndDir;
 using android::vold::EnsureDirExists;
 using android::vold::IsFilesystemSupported;
+using android::vold::IsVirtioBlkDevice;
 using android::vold::PrepareAndroidDirs;
 using android::vold::PrepareAppDirFromRoot;
 using android::vold::PrivateVolume;
@@ -103,8 +104,6 @@
 static const unsigned int kSizeVirtualDisk = 536870912;
 
 static const unsigned int kMajorBlockMmc = 179;
-static const unsigned int kMajorBlockExperimentalMin = 240;
-static const unsigned int kMajorBlockExperimentalMax = 254;
 
 using ScanProcCallback = bool(*)(uid_t uid, pid_t pid, int nsFd, const char* name, void* params);
 
@@ -231,12 +230,10 @@
             for (const auto& source : mDiskSources) {
                 if (source->matches(eventPath)) {
                     // For now, assume that MMC and virtio-blk (the latter is
-                    // emulator-specific; see Disk.cpp for details) devices are SD,
-                    // and that everything else is USB
+                    // specific to virtual platforms; see Utils.cpp for details)
+                    // devices are SD, and that everything else is USB
                     int flags = source->getFlags();
-                    if (major == kMajorBlockMmc || (android::vold::IsRunningInEmulator() &&
-                                                    major >= (int)kMajorBlockExperimentalMin &&
-                                                    major <= (int)kMajorBlockExperimentalMax)) {
+                    if (major == kMajorBlockMmc || IsVirtioBlkDevice(major)) {
                         flags |= android::vold::Disk::Flags::kSd;
                     } else {
                         flags |= android::vold::Disk::Flags::kUsb;
diff --git a/model/Disk.cpp b/model/Disk.cpp
index a4324db..4df4e9d 100644
--- a/model/Disk.cpp
+++ b/model/Disk.cpp
@@ -73,8 +73,6 @@
 static const unsigned int kMajorBlockScsiO = 134;
 static const unsigned int kMajorBlockScsiP = 135;
 static const unsigned int kMajorBlockMmc = 179;
-static const unsigned int kMajorBlockExperimentalMin = 240;
-static const unsigned int kMajorBlockExperimentalMax = 254;
 static const unsigned int kMajorBlockDynamicMin = 234;
 static const unsigned int kMajorBlockDynamicMax = 512;
 
@@ -88,33 +86,6 @@
     kGpt,
 };
 
-static bool isVirtioBlkDevice(unsigned int major) {
-    /*
-     * The new emulator's "ranchu" virtual board no longer includes a goldfish
-     * MMC-based SD card device; instead, it emulates SD cards with virtio-blk,
-     * which has been supported by upstream kernel and QEMU for quite a while.
-     * Unfortunately, the virtio-blk block device 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).
-     *
-     * This makes it difficult for vold to detect a virtio-blk based SD card.
-     * The current solution checks two conditions (both must be met):
-     *
-     *  a) If the running environment is the emulator;
-     *  b) If the major number is an experimental block device major number (for
-     *     x86/x86_64 3.10 ranchu kernels, virtio-blk always gets major number
-     *     253, but it is safer to match the range than just one value).
-     *
-     * Other conditions could be used, too, e.g. the hardware name should be
-     * "ranchu", the device's sysfs path should end with "/block/vd[d-z]", etc.
-     * But just having a) and b) is enough for now.
-     */
-    return IsRunningInEmulator() && major >= kMajorBlockExperimentalMin &&
-           major <= kMajorBlockExperimentalMax;
-}
-
 static bool isNvmeBlkDevice(unsigned int major, const std::string& sysPath) {
     return sysPath.find("nvme") != std::string::npos && major >= kMajorBlockDynamicMin &&
            major <= kMajorBlockDynamicMax;
@@ -322,7 +293,7 @@
             break;
         }
         default: {
-            if (isVirtioBlkDevice(majorId)) {
+            if (IsVirtioBlkDevice(majorId)) {
                 LOG(DEBUG) << "Recognized experimental block major ID " << majorId
                            << " as virtio-blk (emulator's virtual SD card device)";
                 mLabel = "Virtual";
@@ -627,7 +598,7 @@
             return std::stoi(tmp);
         }
         default: {
-            if (isVirtioBlkDevice(majorId)) {
+            if (IsVirtioBlkDevice(majorId)) {
                 // drivers/block/virtio_blk.c has "#define PART_BITS 4", so max is
                 // 2^4 - 1 = 15
                 return 15;