Merge "Support emulator's virtio-blk based SD card" into nyc-dev
diff --git a/Disk.cpp b/Disk.cpp
index 920edab..ebd55da 100644
--- a/Disk.cpp
+++ b/Disk.cpp
@@ -65,6 +65,8 @@
 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 char* kGptBasicData = "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7";
 static const char* kGptAndroidMeta = "19A710A2-B3CA-11E4-B026-10604B889DCF";
@@ -76,6 +78,33 @@
     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;
+}
+
 Disk::Disk(const std::string& eventPath, dev_t device,
         const std::string& nickname, int flags) :
         mDevice(device), mSize(-1), mNickname(nickname), mFlags(flags), mCreated(
@@ -197,7 +226,8 @@
         close(fd);
     }
 
-    switch (major(mDevice)) {
+    unsigned int majorId = major(mDevice);
+    switch (majorId) {
     case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC: case kMajorBlockScsiD:
     case kMajorBlockScsiE: case kMajorBlockScsiF: case kMajorBlockScsiG: case kMajorBlockScsiH:
     case kMajorBlockScsiI: case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:
@@ -231,7 +261,13 @@
         break;
     }
     default: {
-        LOG(WARNING) << "Unsupported block major type" << major(mDevice);
+        if (isVirtioBlkDevice(majorId)) {
+            LOG(DEBUG) << "Recognized experimental block major ID " << majorId
+                    << " as virtio-blk (emulator's virtual SD card device)";
+            mLabel = "Virtual";
+            break;
+        }
+        LOG(WARNING) << "Unsupported block major type " << majorId;
         return -ENOTSUP;
     }
     }
@@ -490,7 +526,8 @@
 
 int Disk::getMaxMinors() {
     // Figure out maximum partition devices supported
-    switch (major(mDevice)) {
+    unsigned int majorId = major(mDevice);
+    switch (majorId) {
     case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC: case kMajorBlockScsiD:
     case kMajorBlockScsiE: case kMajorBlockScsiF: case kMajorBlockScsiG: case kMajorBlockScsiH:
     case kMajorBlockScsiI: case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:
@@ -507,9 +544,16 @@
         }
         return atoi(tmp.c_str());
     }
+    default: {
+        if (isVirtioBlkDevice(majorId)) {
+            // drivers/block/virtio_blk.c has "#define PART_BITS 4", so max is
+            // 2^4 - 1 = 15
+            return 15;
+        }
+    }
     }
 
-    LOG(ERROR) << "Unsupported block major type " << major(mDevice);
+    LOG(ERROR) << "Unsupported block major type " << majorId;
     return -ENOTSUP;
 }
 
diff --git a/Utils.cpp b/Utils.cpp
index 942945d..9d5f168 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -664,5 +664,9 @@
     }
 }
 
+bool IsRunningInEmulator() {
+    return property_get_bool("ro.kernel.qemu", 0);
+}
+
 }  // namespace vold
 }  // namespace android
diff --git a/Utils.h b/Utils.h
index cfc8050..d2970e7 100644
--- a/Utils.h
+++ b/Utils.h
@@ -134,6 +134,9 @@
     DISALLOW_COPY_AND_ASSIGN(ScopedDir);
 };
 
+/* Checks if Android is running in QEMU */
+bool IsRunningInEmulator();
+
 }  // namespace vold
 }  // namespace android
 
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 9304630..5cc60a1 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -91,6 +91,8 @@
 static const char* kUserMountPath = "/mnt/user";
 
 static const unsigned int kMajorBlockMmc = 179;
+static const unsigned int kMajorBlockExperimentalMin = 240;
+static const unsigned int kMajorBlockExperimentalMax = 254;
 
 /* writes superblock at end of file or device given by name */
 static int writeSuperBlock(const char* name, struct asec_superblock *sb, unsigned int numImgSectors) {
@@ -296,10 +298,14 @@
     case NetlinkEvent::Action::kAdd: {
         for (auto source : mDiskSources) {
             if (source->matches(eventPath)) {
-                // For now, assume that MMC devices are SD, and that
-                // everything else is USB
+                // 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
                 int flags = source->getFlags();
-                if (major == kMajorBlockMmc) {
+                if (major == kMajorBlockMmc
+                    || (android::vold::IsRunningInEmulator()
+                    && major >= (int) kMajorBlockExperimentalMin
+                    && major <= (int) kMajorBlockExperimentalMax)) {
                     flags |= android::vold::Disk::Flags::kSd;
                 } else {
                     flags |= android::vold::Disk::Flags::kUsb;