Delete the user key when deleting a user.

BUG=19706593

Change-Id: I36ec1b987f5a07450c6a564c74f124ec8d3403ad
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 35e535d..edae5d9 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1193,6 +1193,21 @@
                     _data.recycle();
                 }
             }
+
+            @Override
+            public void deleteUserKey(int userHandle) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(userHandle);
+                    mRemote.transact(Stub.TRANSACTION_deleteUserKey, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
         }
 
         private static final String DESCRIPTOR = "IMountService";
@@ -1310,6 +1325,8 @@
 
         static final int TRANSACTION_createNewUserDir = IBinder.FIRST_CALL_TRANSACTION + 61;
 
+        static final int TRANSACTION_deleteUserKey = IBinder.FIRST_CALL_TRANSACTION + 62;
+
         /**
          * Cast an IBinder object into an IMountService interface, generating a
          * proxy if needed.
@@ -1871,6 +1888,13 @@
                     reply.writeNoException();
                     return true;
                 }
+                case TRANSACTION_deleteUserKey: {
+                    data.enforceInterface(DESCRIPTOR);
+                    int userHandle = data.readInt();
+                    deleteUserKey(userHandle);
+                    reply.writeNoException();
+                    return true;
+                }
             }
             return super.onTransact(code, data, reply, flags);
         }
@@ -2188,4 +2212,11 @@
      */
     public void createNewUserDir(int userHandle, String path)
         throws RemoteException;
+
+    /**
+     * Securely delete the user's encryption key
+     * @param userHandle Handle of the user whose key we are deleting
+     */
+    public void deleteUserKey(int userHandle)
+        throws RemoteException;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 0502397..6dfc3d3 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -911,6 +911,15 @@
     }
 
     /** {@hide} */
+    public void deleteUserKey(int userHandle) {
+        try {
+            mMountService.deleteUserKey(userHandle);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /** {@hide} */
     public static File maybeTranslateEmulatedPathToInternal(File path) {
         final IMountService mountService = IMountService.Stub.asInterface(
                 ServiceManager.getService("mount"));
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 8c6c25b..7e6dd5b 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2336,6 +2336,35 @@
         }
     }
 
+    // ext4enc:TODO duplication between this and createNewUserDir is nasty
+    @Override
+    public void deleteUserKey(int userHandle) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only SYSTEM_UID can delete user keys");
+        }
+
+        waitForReady();
+
+        if (DEBUG_EVENTS) {
+            Slog.i(TAG, "Deleting user key");
+        }
+
+        try {
+            NativeDaemonEvent event = mConnector.execute(
+                "cryptfs", "deleteuserkey", userHandle);
+            if (!"0".equals(event.getMessage())) {
+                String error = "deleteuserkey sent unexpected message: "
+                    + event.getMessage();
+                Slog.e(TAG,  error);
+                // ext4enc:TODO is this the right exception?
+                throw new RuntimeException(error);
+            }
+        } catch (NativeDaemonConnectorException e) {
+            Slog.e(TAG, "deleteuserkey threw exception", e);
+            throw new RuntimeException("deleteuserkey threw exception", e);
+        }
+    }
+
     @Override
     public int mkdirs(String callingPkg, String appPath) {
         final int userId = UserHandle.getUserId(Binder.getCallingUid());
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 15d1535a..d859442 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -42,6 +42,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.StorageManager;
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Slog;
@@ -1444,6 +1445,8 @@
     }
 
     private void removeUserStateLocked(final int userHandle) {
+        mContext.getSystemService(StorageManager.class)
+            .deleteUserKey(userHandle);
         // Cleanup package manager settings
         mPm.cleanUpUserLILPw(this, userHandle);