Emulated volumes above private volumes.

When a private volume is mounted, create an emulated volume above it
hosted at the /media path on that device.  That emulated volume is
automatically torn down when unmounting the private volume.

Add "removed" state for volume, which signals to framework that
media has left the building, send when the volume is destroyed.

Bug: 19993667
Change-Id: I1f82b51de578ac5cfcc5d7b9a6fb44f6f25c775c
diff --git a/Disk.cpp b/Disk.cpp
index 794e1dd..5d53304 100644
--- a/Disk.cpp
+++ b/Disk.cpp
@@ -163,6 +163,7 @@
 
 void Disk::destroyAllVolumes() {
     for (auto vol : mVolumes) {
+        notifyEvent(ResponseCode::DiskVolumeDestroyed, vol->getId());
         vol->destroy();
     }
     mVolumes.clear();
diff --git a/EmulatedVolume.cpp b/EmulatedVolume.cpp
index 5dcab72..bb53266 100644
--- a/EmulatedVolume.cpp
+++ b/EmulatedVolume.cpp
@@ -36,16 +36,18 @@
 
 static const char* kFusePath = "/system/bin/sdcard";
 
-EmulatedVolume::EmulatedVolume(const std::string& rawPath,
-        const std::string& fsUuid) : VolumeBase(Type::kEmulated), mFusePid(0) {
-    if (fsUuid.empty()) {
-        setId("emulated");
-    } else {
-        setId(StringPrintf("emulated:%s", fsUuid.c_str()));
-    }
-
+EmulatedVolume::EmulatedVolume(const std::string& rawPath) :
+        VolumeBase(Type::kEmulated), mFusePid(0) {
+    setId("emulated");
+    mFusePath = "/storage/emulated";
     mRawPath = rawPath;
-    mFusePath = StringPrintf("/storage/%s", getId().c_str());
+}
+
+EmulatedVolume::EmulatedVolume(const std::string& rawPath, dev_t device,
+        const std::string& fsUuid) : VolumeBase(Type::kEmulated), mFusePid(0) {
+    setId(StringPrintf("emulated:%u,%u", major(device), minor(device)));
+    mFusePath = StringPrintf("/storage/%s", fsUuid.c_str());
+    mRawPath = rawPath;
 }
 
 EmulatedVolume::~EmulatedVolume() {
diff --git a/EmulatedVolume.h b/EmulatedVolume.h
index 3e28bcb..04d4508 100644
--- a/EmulatedVolume.h
+++ b/EmulatedVolume.h
@@ -37,7 +37,8 @@
  */
 class EmulatedVolume : public VolumeBase {
 public:
-    EmulatedVolume(const std::string& rawPath, const std::string& fsUuid);
+    EmulatedVolume(const std::string& rawPath);
+    EmulatedVolume(const std::string& rawPath, dev_t device, const std::string& fsUuid);
     virtual ~EmulatedVolume();
 
 protected:
diff --git a/PrivateVolume.cpp b/PrivateVolume.cpp
index 6ca5480..2b3b7b6 100644
--- a/PrivateVolume.cpp
+++ b/PrivateVolume.cpp
@@ -16,6 +16,7 @@
 
 #include "Ext4.h"
 #include "PrivateVolume.h"
+#include "EmulatedVolume.h"
 #include "Utils.h"
 #include "VolumeManager.h"
 #include "ResponseCode.h"
@@ -120,6 +121,14 @@
         return -EIO;
     }
 
+    // Create a new emulated volume stacked above us, it will automatically
+    // be destroyed during unmount
+    std::string mediaPath(mPath + "/media");
+    auto vol = std::shared_ptr<VolumeBase>(
+            new EmulatedVolume(mediaPath, mRawDevice, mFsUuid));
+    addVolume(vol);
+    vol->create();
+
     return OK;
 }
 
diff --git a/ResponseCode.h b/ResponseCode.h
index 567b7a8..cdb4a79 100644
--- a/ResponseCode.h
+++ b/ResponseCode.h
@@ -70,6 +70,7 @@
     static const int DiskSizeChanged = 641;
     static const int DiskLabelChanged = 642;
     static const int DiskVolumeCreated = 643;
+    static const int DiskVolumeDestroyed = 644;
     static const int DiskDestroyed = 649;
 
     static const int VolumeCreated = 650;
diff --git a/VolumeBase.cpp b/VolumeBase.cpp
index de5b072..3b49b0d 100644
--- a/VolumeBase.cpp
+++ b/VolumeBase.cpp
@@ -135,6 +135,7 @@
     mCreated = true;
     status_t res = doCreate();
     notifyEvent(ResponseCode::VolumeCreated, StringPrintf("%d", mType));
+    setState(State::kUnmounted);
     return res;
 }
 
@@ -149,6 +150,7 @@
         unmount();
     }
 
+    setState(State::kRemoved);
     notifyEvent(ResponseCode::VolumeDestroyed);
     status_t res = doDestroy();
     mCreated = false;
@@ -185,8 +187,8 @@
     setState(State::kUnmounting);
 
     for (auto vol : mVolumes) {
-        if (vol->unmount()) {
-            LOG(WARNING) << getId() << " failed to unmount " << vol->getId()
+        if (vol->destroy()) {
+            LOG(WARNING) << getId() << " failed to destroy " << vol->getId()
                     << " stacked above";
         }
     }
diff --git a/VolumeBase.h b/VolumeBase.h
index 1494c2c..e7c7836 100644
--- a/VolumeBase.h
+++ b/VolumeBase.h
@@ -75,6 +75,8 @@
         kUnmounting,
         /* Next states: mounting, formatting */
         kUnmountable,
+        /* Next states: none */
+        kRemoved,
     };
 
     const std::string& getId() { return mId; }
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 5b7ce22..86e3fd4 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -260,7 +260,7 @@
     // Assume that we always have an emulated volume on internal
     // storage; the framework will decide if it should be mounted.
     mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(
-            new android::vold::EmulatedVolume("/data/media", ""));
+            new android::vold::EmulatedVolume("/data/media"));
     mInternalEmulated->create();
 
     return 0;