vold: Add 'force' option to anything that can cause an unmount

Signed-off-by: San Mehat <san@google.com>
diff --git a/CommandListener.cpp b/CommandListener.cpp
index b25728f..c87198e 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -59,7 +59,11 @@
     } else if (!strcmp(argv[1], "mount")) {
         rc = vm->mountVolume(argv[2]);
     } else if (!strcmp(argv[1], "unmount")) {
-        rc = vm->unmountVolume(argv[2]);
+        bool force = false;
+        if (argc >= 4 && !strcmp(argv[3], "force")) {
+            force = true;
+        }
+        rc = vm->unmountVolume(argv[2], force);
     } else if (!strcmp(argv[1], "format")) {
         rc = vm->formatVolume(argv[2]);
     } else if (!strcmp(argv[1], "share")) {
@@ -244,11 +248,15 @@
             cli->sendMsg(ResponseCode::CommandOkay, "Container finalized", false);
         }
     } else if (!strcmp(argv[1], "destroy")) {
-        if (argc != 3) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec destroy <container-id>", false);
+        if (argc < 3) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec destroy <container-id> [force]", false);
             return 0;
         }
-        if (vm->destroyAsec(argv[2])) {
+        bool force = false;
+        if (argc > 3 && !strcmp(argv[3], "force")) {
+            force = true;
+        }
+        if (vm->destroyAsec(argv[2], force)) {
             cli->sendMsg(ResponseCode::OperationFailed, "Container destroy failed", true);
         } else {
             cli->sendMsg(ResponseCode::CommandOkay, "Container destroyed", false);
@@ -267,13 +275,16 @@
         } else {
             cli->sendMsg(ResponseCode::CommandOkay, "Mount succeeded", false);
         }
-
     } else if (!strcmp(argv[1], "unmount")) {
-        if (argc != 3) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec unmount <container-id>", false);
+        if (argc < 3) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec unmount <container-id> [force]", false);
             return 0;
         }
-        if (vm->unmountAsec(argv[2])) {
+        bool force = false;
+        if (argc > 3 && !strcmp(argv[3], "force")) {
+            force = true;
+        }
+        if (vm->unmountAsec(argv[2], force)) {
             cli->sendMsg(ResponseCode::OperationFailed, "Container unmount failed", true);
         } else {
             cli->sendMsg(ResponseCode::CommandOkay, "Container unmounted", false);
diff --git a/DirectVolume.cpp b/DirectVolume.cpp
index 68a6fcd..dcb1a05 100644
--- a/DirectVolume.cpp
+++ b/DirectVolume.cpp
@@ -274,7 +274,7 @@
                  getLabel(), getMountpoint(), major, minor);
         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
                                              msg, false);
-        if (Volume::unmountVol()) {
+        if (Volume::unmountVol(true)) {
             LOGE("Failed to unmount volume on bad removal (%s)", 
                  strerror(errno));
             // XXX: At this point we're screwed for now
diff --git a/Volume.cpp b/Volume.cpp
index 529975a..cce2fc1 100644
--- a/Volume.cpp
+++ b/Volume.cpp
@@ -275,7 +275,7 @@
     return -1;
 }
 
-int Volume::unmountVol() {
+int Volume::unmountVol(bool force) {
     int i, rc;
 
     if (getState() != Volume::State_Mounted) {
@@ -285,7 +285,7 @@
     }
 
     setState(Volume::State_Unmounting);
-    usleep(1000 * 200); // Give the framework some time to react
+    usleep(1000 * 1000); // Give the framework some time to react
     for (i = 1; i <= 10; i++) {
         rc = umount(getMountpoint());
         if (!rc)
@@ -299,17 +299,18 @@
         LOGW("Volume %s unmount attempt %d failed (%s)",
              getLabel(), i, strerror(errno));
 
-        int action;
+        int action = 0;
 
-        if (i > 8) {
-            action = 2; // SIGKILL
-        } else if (i > 7) {
-            action = 1; // SIGHUP
-        } else
-            action = 0; // just complain
+        if (force) {
+            if (i > 8) {
+                action = 2; // SIGKILL
+            } else if (i > 7) {
+                action = 1; // SIGHUP
+            }
+        }
 
         Process::killProcessesWithOpenFiles(getMountpoint(), action);
-        usleep(1000*250);
+        usleep(1000*1000);
     }
 
     if (!rc) {
@@ -319,6 +320,7 @@
         return 0;
     }
 
+    errno = EBUSY;
     LOGE("Volume %s failed to unmount (%s)\n", getLabel(), strerror(errno));
     setState(Volume::State_Mounted);
     return -1;
diff --git a/Volume.h b/Volume.h
index 7306e4e..9bf0f0a 100644
--- a/Volume.h
+++ b/Volume.h
@@ -53,7 +53,7 @@
     virtual ~Volume();
 
     int mountVol();
-    int unmountVol();
+    int unmountVol(bool force);
     int formatVol();
 
     const char *getLabel() { return mLabel; }
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 251e11c..72ec3da 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -325,8 +325,8 @@
     return -1;
 }
 
-#define ASEC_UNMOUNT_RETRIES 10
-int VolumeManager::unmountAsec(const char *id) {
+#define ASEC_UNMOUNT_RETRIES 5
+int VolumeManager::unmountAsec(const char *id, bool force) {
     char asecFileName[255];
     char mountPoint[255];
 
@@ -353,20 +353,22 @@
         LOGW("ASEC %s unmount attempt %d failed (%s)",
               id, i, strerror(errno));
 
-        int action;
-        if (i > (ASEC_UNMOUNT_RETRIES - 2))
-            action = 2; // SIGKILL
-        else if (i > (ASEC_UNMOUNT_RETRIES - 3))
-            action = 1; // SIGHUP
-        else
-            action = 0; // Just complain
+        int action = 0; // default is to just complain
+
+        if (force) {
+            if (i > (ASEC_UNMOUNT_RETRIES - 2))
+                action = 2; // SIGKILL
+            else if (i > (ASEC_UNMOUNT_RETRIES - 3))
+                action = 1; // SIGHUP
+        }
 
         Process::killProcessesWithOpenFiles(mountPoint, action);
         usleep(1000 * 1000);
     }
 
     if (rc) {
-        LOGE("Failed to unmount ASEC %s", id);
+        errno = EBUSY;
+        LOGE("Failed to unmount container %s (%s)", id, strerror(errno));
         return -1;
     }
 
@@ -397,7 +399,7 @@
     return 0;
 }
 
-int VolumeManager::destroyAsec(const char *id) {
+int VolumeManager::destroyAsec(const char *id, bool force) {
     char asecFileName[255];
     char mountPoint[255];
 
@@ -407,7 +409,7 @@
 
     if (isMountpointMounted(mountPoint)) {
         LOGD("Unmounting container before destroy");
-        if (unmountAsec(id)) {
+        if (unmountAsec(id, force)) {
             LOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno));
             return -1;
         }
@@ -675,7 +677,7 @@
     return 0;
 }
 
-int VolumeManager::unmountVolume(const char *label) {
+int VolumeManager::unmountVolume(const char *label, bool force) {
     Volume *v = lookupVolume(label);
 
     if (!v) {
@@ -698,13 +700,13 @@
     while(mActiveContainers->size()) {
         AsecIdCollection::iterator it = mActiveContainers->begin();
         LOGI("Unmounting ASEC %s (dependant on %s)", *it, v->getMountpoint());
-        if (unmountAsec(*it)) {
+        if (unmountAsec(*it, force)) {
             LOGE("Failed to unmount ASEC %s (%s) - unmount of %s may fail!", *it,
                  strerror(errno), v->getMountpoint());
         }
     }
 
-    return v->unmountVol();
+    return v->unmountVol(force);
 }
 
 /*
diff --git a/VolumeManager.h b/VolumeManager.h
index f989cdb..5ed6f21 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -52,7 +52,7 @@
 
     int listVolumes(SocketClient *cli);
     int mountVolume(const char *label);
-    int unmountVolume(const char *label);
+    int unmountVolume(const char *label, bool force);
     int shareVolume(const char *label, const char *method);
     int unshareVolume(const char *label, const char *method);
     int shareAvailable(const char *method, bool *avail);
@@ -62,9 +62,9 @@
     int createAsec(const char *id, unsigned numSectors, const char *fstype,
                    const char *key, int ownerUid);
     int finalizeAsec(const char *id);
-    int destroyAsec(const char *id);
+    int destroyAsec(const char *id, bool force);
     int mountAsec(const char *id, const char *key, int ownerUid);
-    int unmountAsec(const char *id);
+    int unmountAsec(const char *id, bool force);
     int renameAsec(const char *id1, const char *id2);
     int getAsecMountPath(const char *id, char *buffer, int maxlen);