Add decryption support to MountService.

* Implement the decryptStorage() method on the Mount Service.
  This method makes the calls into vold to decrypt the encrypted
  volumes and mount them.

Change-Id: I4f6e07a111cf0b36611d590debf9f6579c5ac5f7
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 3a2add4..23ed31f 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -586,6 +586,23 @@
                 }
                 return _result;
             }
+
+            public int decryptStorage(String password) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(password);
+                    mRemote.transact(Stub.TRANSACTION_decryptStorage, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
         }
 
         private static final String DESCRIPTOR = "IMountService";
@@ -642,6 +659,8 @@
 
         static final int TRANSACTION_isExternalStorageEmulated = IBinder.FIRST_CALL_TRANSACTION + 25;
 
+        static final int TRANSACTION_decryptStorage = IBinder.FIRST_CALL_TRANSACTION + 26;
+
         /**
          * Cast an IBinder object into an IMountService interface, generating a
          * proxy if needed.
@@ -923,6 +942,14 @@
                     reply.writeInt(emulated ? 1 : 0);
                     return true;
                 }
+                case TRANSACTION_decryptStorage: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String password = data.readString();
+                    int result = decryptStorage(password);
+                    reply.writeNoException();
+                    reply.writeInt(result);
+                    return true;
+                }
             }
             return super.onTransact(code, data, reply, flags);
         }
@@ -1082,4 +1109,9 @@
      * Returns whether or not the external storage is emulated.
      */
     public boolean isExternalStorageEmulated() throws RemoteException;
+
+    /**
+     * Decrypts any encrypted volumes.
+     */
+    public int decryptStorage(String password) throws RemoteException;
 }
diff --git a/include/storage/IMountService.h b/include/storage/IMountService.h
index 51f9aeb..68ccd95 100644
--- a/include/storage/IMountService.h
+++ b/include/storage/IMountService.h
@@ -66,6 +66,7 @@
             const sp<IObbActionListener>& token, const int32_t nonce) = 0;
     virtual bool isObbMounted(const String16& filename) = 0;
     virtual bool getMountedObbPath(const String16& filename, String16& path) = 0;
+    virtual int32_t decryptStorage(const String16& password) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index b5f9c47..a7ab824 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -48,6 +48,7 @@
     TRANSACTION_isObbMounted,
     TRANSACTION_getMountedObbPath,
     TRANSACTION_isExternalStorageEmulated,
+    TRANSACTION_decryptStorage,
 };
 
 class BpMountService: public BpInterface<IMountService>
@@ -504,6 +505,23 @@
         path = reply.readString16();
         return true;
     }
+    
+    int32_t decryptStorage(const String16& password)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(password);
+        if (remote()->transact(TRANSACTION_decryptStorage, data, &reply) != NO_ERROR) {
+            LOGD("decryptStorage could not contact remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("decryptStorage caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
 };
 
 IMPLEMENT_META_INTERFACE(MountService, "IMountService");
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 64cd661..bf81457 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -73,8 +73,8 @@
  * @hide - Applications should use android.os.storage.StorageManager
  * to access the MountService.
  */
-class MountService extends IMountService.Stub
-        implements INativeDaemonConnectorCallbacks {
+class MountService extends IMountService.Stub implements INativeDaemonConnectorCallbacks {
+
     private static final boolean LOCAL_LOGD = false;
     private static final boolean DEBUG_UNMOUNT = false;
     private static final boolean DEBUG_EVENTS = false;
@@ -334,6 +334,7 @@
             super(l);
         }
 
+        @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case H_UNMOUNT_PM_UPDATE: {
@@ -427,6 +428,7 @@
     }
 
     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
 
@@ -442,6 +444,7 @@
                     return;
                 }
                 new Thread() {
+                    @Override
                     public void run() {
                         try {
                             String path = Environment.getExternalStorageDirectory().getPath();
@@ -565,6 +568,7 @@
          * we need to do our work in a new thread.
          */
         new Thread() {
+            @Override
             public void run() {
                 /**
                  * Determine media state and UMS detection status
@@ -678,6 +682,7 @@
 
             if (code == VoldResponseCode.VolumeDiskInserted) {
                 new Thread() {
+                    @Override
                     public void run() {
                         try {
                             int rc;
@@ -1007,6 +1012,7 @@
              * USB mass storage disconnected while enabled
              */
             new Thread() {
+                @Override
                 public void run() {
                     try {
                         int rc;
@@ -1624,6 +1630,29 @@
             Slog.i(TAG, "Send to OBB handler: " + action.toString());
     }
 
+    public int decryptStorage(String password) {
+        if (password == null) {
+            throw new IllegalArgumentException("password cannot be null");
+        }
+
+        // TODO: Enforce a permission
+
+        waitForReady();
+
+        if (DEBUG_EVENTS) {
+            Slog.i(TAG, "decrypting storage...");
+        }
+
+        try {
+            mConnector.doCommand(String.format("cryptfs checkpw %s", password));
+        } catch (NativeDaemonConnectorException e) {
+            // Decryption failed
+            return e.getCode();
+        }
+
+        return 0;
+    }
+
     private void addObbStateLocked(ObbState obbState) throws RemoteException {
         final IBinder binder = obbState.getBinder();
         List<ObbState> obbStates = mObbMounts.get(binder);
@@ -1911,6 +1940,7 @@
             mKey = key;
         }
 
+        @Override
         public void handleExecute() throws IOException, RemoteException {
             waitForReady();
             warnOnNotMounted();
@@ -1991,6 +2021,7 @@
             }
         }
 
+        @Override
         public void handleError() {
             sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
         }
@@ -2020,6 +2051,7 @@
             mForceUnmount = force;
         }
 
+        @Override
         public void handleExecute() throws IOException {
             waitForReady();
             warnOnNotMounted();
@@ -2074,6 +2106,7 @@
             }
         }
 
+        @Override
         public void handleError() {
             sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
         }