Merge "Revert encryption mapping for device wipes."
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index e0130b5..d1dc6e5 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -169,13 +169,15 @@
              * is an asynchronous operation. Applications should register
              * StorageEventListener for storage related status changes.
              */
-            public void unmountVolume(String mountPoint, boolean force) throws RemoteException {
+            public void unmountVolume(String mountPoint, boolean force, boolean removeEncryption)
+                    throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
                 try {
                     _data.writeInterfaceToken(DESCRIPTOR);
                     _data.writeString(mountPoint);
                     _data.writeInt((force ? 1 : 0));
+                    _data.writeInt((removeEncryption ? 1 : 0));
                     mRemote.transact(Stub.TRANSACTION_unmountVolume, _data, _reply, 0);
                     _reply.readException();
                 } finally {
@@ -842,9 +844,9 @@
                     data.enforceInterface(DESCRIPTOR);
                     String mountPoint;
                     mountPoint = data.readString();
-                    boolean force;
-                    force = 0 != data.readInt();
-                    unmountVolume(mountPoint, force);
+                    boolean force = 0 != data.readInt();
+                    boolean removeEncrypt = 0 != data.readInt();
+                    unmountVolume(mountPoint, force, removeEncrypt);
                     reply.writeNoException();
                     return true;
                 }
@@ -1234,8 +1236,14 @@
      * Safely unmount external storage at given mount point. The unmount is an
      * asynchronous operation. Applications should register StorageEventListener
      * for storage related status changes.
+     * @param mountPoint the mount point
+     * @param force whether or not to forcefully unmount it (e.g. even if programs are using this
+     *     data currently)
+     * @param removeEncryption whether or not encryption mapping should be removed from the volume.
+     *     This value implies {@code force}.
      */
-    public void unmountVolume(String mountPoint, boolean force) throws RemoteException;
+    public void unmountVolume(String mountPoint, boolean force, boolean removeEncryption)
+            throws RemoteException;
 
     /**
      * Unregisters an IMountServiceListener
diff --git a/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java b/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java
index 4773ce4..3905c88 100644
--- a/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java
+++ b/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java
@@ -152,7 +152,8 @@
                     Environment.getExternalStorageDirectory().toString() :
                     mStorageVolume.getPath();
             try {
-                mountService.unmountVolume(extStoragePath, true);
+                // Remove encryption mapping if this is an unmount for a factory reset.
+                mountService.unmountVolume(extStoragePath, true, mFactoryReset);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with mount service", e);
             }
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 6c87c3b..68ddcc4 100755
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -1057,7 +1057,7 @@
         try {
             // Wait on observer
             synchronized(observer) {
-                getMs().unmountVolume(path, true);
+                getMs().unmountVolume(path, true, false);
                 long waitTime = 0;
                 while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
                     observer.wait(WAIT_TIME_INCR);
diff --git a/core/tests/coretests/src/android/os/storage/AsecTests.java b/core/tests/coretests/src/android/os/storage/AsecTests.java
index dda3010..5efbd88 100755
--- a/core/tests/coretests/src/android/os/storage/AsecTests.java
+++ b/core/tests/coretests/src/android/os/storage/AsecTests.java
@@ -421,7 +421,7 @@
         try {
             // Wait on observer
             synchronized(observer) {
-                getMs().unmountVolume(path, false);
+                getMs().unmountVolume(path, false, false);
                 long waitTime = 0;
                 while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
                     observer.wait(WAIT_TIME_INCR);
@@ -486,7 +486,7 @@
             // Wait on observer
             synchronized(observer) {
                 for (int i = 0; i < 5; i++) {
-                    getMs().unmountVolume(path, false);
+                    getMs().unmountVolume(path, false, false);
                 }
                 long waitTime = 0;
                 while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
diff --git a/include/storage/IMountService.h b/include/storage/IMountService.h
index 472d8e5..43df7f0 100644
--- a/include/storage/IMountService.h
+++ b/include/storage/IMountService.h
@@ -37,8 +37,8 @@
     virtual void setUsbMassStorageEnabled(const bool enable) = 0;
     virtual bool isUsbMassStorageEnabled() = 0;
     virtual int32_t mountVolume(const String16& mountPoint) = 0;
-    virtual int32_t
-            unmountVolume(const String16& mountPoint, const bool force) = 0;
+    virtual int32_t unmountVolume(
+            const String16& mountPoint, const bool force, const bool removeEncryption) = 0;
     virtual int32_t formatVolume(const String16& mountPoint) = 0;
     virtual int32_t
             getStorageUsers(const String16& mountPoint, int32_t** users) = 0;
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index 7fbf67a..8ddbeae 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -157,12 +157,13 @@
         return reply.readInt32();
     }
 
-    int32_t unmountVolume(const String16& mountPoint, const bool force)
+    int32_t unmountVolume(const String16& mountPoint, const bool force, const bool removeEncryption)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
         data.writeString16(mountPoint);
         data.writeInt32(force ? 1 : 0);
+        data.writeInt32(removeEncryption ? 1 : 0);
         if (remote()->transact(TRANSACTION_unmountVolume, data, &reply) != NO_ERROR) {
             LOGD("unmountVolume could not contact remote\n");
             return -1;
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index d806309..582f0ed 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -304,17 +304,19 @@
     class UnmountCallBack {
         final String path;
         final boolean force;
+        final boolean removeEncryption;
         int retries;
 
-        UnmountCallBack(String path, boolean force) {
+        UnmountCallBack(String path, boolean force, boolean removeEncryption) {
             retries = 0;
             this.path = path;
             this.force = force;
+            this.removeEncryption = removeEncryption;
         }
 
         void handleFinished() {
             if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
-            doUnmountVolume(path, true);
+            doUnmountVolume(path, true, removeEncryption);
         }
     }
 
@@ -322,7 +324,7 @@
         final String method;
 
         UmsEnableCallBack(String path, String method, boolean force) {
-            super(path, force);
+            super(path, force, false);
             this.method = method;
         }
 
@@ -336,13 +338,13 @@
     class ShutdownCallBack extends UnmountCallBack {
         IMountShutdownObserver observer;
         ShutdownCallBack(String path, IMountShutdownObserver observer) {
-            super(path, true);
+            super(path, true, false);
             this.observer = observer;
         }
 
         @Override
         void handleFinished() {
-            int ret = doUnmountVolume(path, true);
+            int ret = doUnmountVolume(path, true, removeEncryption);
             if (observer != null) {
                 try {
                     observer.onShutDownComplete(ret);
@@ -888,8 +890,10 @@
      * This might even take a while and might be retried after timed delays
      * to make sure we dont end up in an instable state and kill some core
      * processes.
+     * If removeEncryption is set, force is implied, and the system will remove any encryption
+     * mapping set on the volume when unmounting.
      */
-    private int doUnmountVolume(String path, boolean force) {
+    private int doUnmountVolume(String path, boolean force, boolean removeEncryption) {
         if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
             return VoldResponseCode.OpFailedVolNotMounted;
         }
@@ -905,8 +909,10 @@
         // Redundant probably. But no harm in updating state again.
         mPms.updateExternalMediaStatus(false, false);
         try {
-            mConnector.doCommand(String.format(
-                    "volume unmount %s%s", path, (force ? " force" : "")));
+            String arg = removeEncryption
+                    ? " force_and_revert"
+                    : (force ? " force" : "");
+            mConnector.doCommand(String.format("volume unmount %s%s", path, arg));
             // We unmounted the volume. None of the asec containers are available now.
             synchronized (mAsecMountSet) {
                 mAsecMountSet.clear();
@@ -1371,12 +1377,16 @@
         return doMountVolume(path);
     }
 
-    public void unmountVolume(String path, boolean force) {
+    public void unmountVolume(String path, boolean force, boolean removeEncryption) {
         validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
         waitForReady();
 
         String volState = getVolumeState(path);
-        if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path + " force = " + force);
+        if (DEBUG_UNMOUNT) {
+            Slog.i(TAG, "Unmounting " + path
+                    + " force = " + force
+                    + " removeEncryption = " + removeEncryption);
+        }
         if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
                 Environment.MEDIA_REMOVED.equals(volState) ||
                 Environment.MEDIA_SHARED.equals(volState) ||
@@ -1385,7 +1395,7 @@
             // TODO return valid return code when adding observer call back.
             return;
         }
-        UnmountCallBack ucb = new UnmountCallBack(path, force);
+        UnmountCallBack ucb = new UnmountCallBack(path, force, removeEncryption);
         mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
     }