framework: MountService: Add initial support for Android Secure External Caches

Signed-off-by: San Mehat <san@google.com>
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl
index 447e764..1ea7200 100644
--- a/core/java/android/os/IMountService.aidl
+++ b/core/java/android/os/IMountService.aidl
@@ -80,4 +80,39 @@
      * Gets the state of an volume via it's mountpoint.
      */
     String getVolumeState(String mountPoint);
+
+    /*
+     * Creates a secure cache with the specified parameters.
+     * On success, the filesystem cache-path is returned.
+     */
+    String createSecureCache(String id, int sizeMb, String fstype, String key, int ownerUid);
+
+    /*
+     * Finalize a cache which has just been created and populated.
+     * After finalization, the cache is immutable.
+     */
+    void finalizeSecureCache(String id);
+
+    /*
+     * Destroy a secure cache, and free up all resources associated with it.
+     * NOTE: Ensure all references are released prior to deleting.
+     */
+    void destroySecureCache(String id);
+
+    /*
+     * Mount a secure cache with the specified key and owner UID.
+     * On success, the filesystem cache-path is returned.
+     */
+    String mountSecureCache(String id, String key, int ownerUid);
+
+    /*
+     * Returns the filesystem path of a mounted secure cache.
+     */
+    String getSecureCachePath(String id);
+
+    /**
+     * Gets an Array of currently known secure cache IDs
+     */
+    String[] getSecureCacheList();
+
 }
diff --git a/media/sdutils/sdutil.cpp b/media/sdutils/sdutil.cpp
index fe11878..7f0dda7 100644
--- a/media/sdutils/sdutil.cpp
+++ b/media/sdutils/sdutil.cpp
@@ -99,6 +99,42 @@
     return -1;
 }
 
+static int asec_create(const char *id, int sizeMb, const char *fstype,
+                       const char *key, int ownerUid) {
+    String16 sId(id);
+    String16 sFstype(fstype);
+    String16 sKey(key);
+
+    String16 r = gMountService->createSecureCache(sId, sizeMb, sFstype,
+                                                  sKey, ownerUid);
+    return 0;
+}
+
+static int asec_finalize(const char *id) {
+    String16 sId(id);
+    gMountService->finalizeSecureCache(sId);
+    return 0;
+}
+
+static int asec_destroy(const char *id) {
+    String16 sId(id);
+    gMountService->destroySecureCache(sId);
+    return 0;
+}
+
+static int asec_mount(const char *id, const char *key, int ownerUid) {
+    String16 sId(id);
+    String16 sKey(key);
+    gMountService->mountSecureCache(sId, sKey, ownerUid);
+    return 0;
+}
+
+static int asec_path(const char *id) {
+    String16 sId(id);
+    gMountService->getSecureCachePath(sId);
+    return 0;
+}
+
 static int unmount(const char* path) {
     String16 string(path);
     gMountService->unmountMedia(string);
@@ -153,14 +189,42 @@
             android::init();
             return android::umsEnable(false);
         }
+    } else if (!strcmp(command, "asec")) {
+        const char* id = (argc > 3 ? argv[3] : NULL);
+
+        if (!id)
+            goto usage;
+
+        android::init();
+        if (!strcmp(argument, "create")) {
+
+            if (argc != 8)
+                goto usage;
+            return android::asec_create(id, atoi(argv[4]), argv[5], argv[6],
+                                        atoi(argv[7]));
+        } else if (!strcmp(argument, "finalize")) {
+            return android::asec_finalize(id);
+        } else if (!strcmp(argument, "destroy")) {
+            return android::asec_destroy(id);
+        } else if (!strcmp(argument, "mount")) {
+            return android::asec_mount(id, argv[4], atoi(argv[5]));
+        } else if (!strcmp(argument, "path")) {
+            return android::asec_path(id);
+        }
     }
     
+usage:
     fprintf(stderr, "usage:\n"
                     "    sdutil mount <mount path>          - mounts the SD card at the given mount point\n"
                     "    sdutil unmount <mount path>        - unmounts the SD card at the given mount point\n"
                     "    sdutil format <mount path>         - formats the SD card at the given mount point\n"
                     "    sdutil ums enable                  - enables USB mass storage\n"
-                    "    sdutil ums disable                 - disnables USB mass storage\n"
+                    "    sdutil ums disable                 - disables USB mass storage\n"
+                    "    sdutil asec create <id> <sizeMb> <fstype> <key> <ownerUid>\n"
+                    "    sdutil asec finalize <id>\n"
+                    "    sdutil asec destroy <id>\n"
+                    "    sdutil asec mount <id> <key> <ownerUid>\n"
+                    "    sdutil asec path <id>\n"
                     );
     return -1;
 }
diff --git a/services/java/com/android/server/MountListener.java b/services/java/com/android/server/MountListener.java
index d4943ff..5a1107b 100644
--- a/services/java/com/android/server/MountListener.java
+++ b/services/java/com/android/server/MountListener.java
@@ -21,7 +21,6 @@
 import android.os.Environment;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.os.RemoteException;
 import android.util.Config;
 import android.util.Log;
 
@@ -29,6 +28,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.Socket;
+import java.lang.IllegalStateException;
 
 import java.util.List;
 import java.util.ArrayList;
@@ -49,7 +49,13 @@
     private OutputStream          mOutputStream;
 
     class ResponseCode {
+        public static final int ActionInitiated                = 100;
+        public static final int VolumeListResult               = 110;
+        public static final int AsecListResult                 = 111;
+
+        public static final int CommandOkay                    = 200;
         public static final int ShareAvailabilityResult        = 210;
+        public static final int AsecPathResult                 = 211;
 
         public static final int UnsolicitedInformational       = 600;
         public static final int VolumeStateChange              = 605;
@@ -163,7 +169,8 @@
         SystemClock.sleep(5000);
     }
 
-    private void handleUnsolicitedEvent(int code, String raw, String[] cooked) throws RemoteException {
+    private void handleUnsolicitedEvent(int code, String raw,
+                                        String[] cooked) throws IllegalStateException {
 //        Log.d(TAG, "unsolicited {" + raw + "}");
         if (code == ResponseCode.VolumeStateChange) {
             // FMT: NNN Volume <label> <mountpoint> state changed from <old_#> (<old_str>) to <new_#> (<new_str>)
@@ -232,7 +239,7 @@
         }
     }
 
-    private synchronized ArrayList<String> doCommand(String cmd) throws RemoteException {
+    private synchronized ArrayList<String> doCommand(String cmd) throws IllegalStateException {
         sendCommand(cmd);
 
         ArrayList<String> response = new ArrayList<String>();
@@ -255,13 +262,14 @@
         }
 
         if (code >= 400 && code < 600) {
-            Log.w(TAG, "Vold cmd {" + cmd + "} err code " + code);
-            throw new RemoteException();
+            throw new IllegalStateException(String.format(
+                                               "Command %s failed with code %d",
+                                                cmd, code));
         }
         return response;
     }
 
-    boolean getShareAvailable(String method) throws RemoteException {
+    boolean getShareAvailable(String method) throws IllegalStateException  {
         ArrayList<String> rsp = doCommand("share_available " + method);
 
         for (String line : rsp) {
@@ -272,10 +280,10 @@
                     return true;
                 return false;
             } else {
-                throw new RemoteException();
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
             }
         }
-        throw new RemoteException();
+        throw new IllegalStateException("Got an empty response");
     }
 
     /**
@@ -283,28 +291,87 @@
      * 
      * @param enable  true to enable USB mass storage support
      */
-    void setShareMethodEnabled(String mountPoint, String method, boolean enable) throws RemoteException {
+    void setShareMethodEnabled(String mountPoint, String method,
+                               boolean enable) throws IllegalStateException {
         doCommand((enable ? "" : "un") + "share " + mountPoint + " " + method);
     }
 
     /**
      * Mount media at given mount point.
      */
-    public void mountVolume(String label) throws RemoteException {
+    public void mountVolume(String label) throws IllegalStateException {
         doCommand("mount " + label);
     }
 
     /**
      * Unmount media at given mount point.
      */
-    public void unmountVolume(String label) throws RemoteException {
+    public void unmountVolume(String label) throws IllegalStateException {
         doCommand("unmount " + label);
     }
 
     /**
      * Format media at given mount point.
      */
-    public void formatVolume(String label) throws RemoteException {
+    public void formatVolume(String label) throws IllegalStateException {
         doCommand("format " + label);
     }
+
+    public String createAsec(String id, int sizeMb, String fstype, String key,
+                           int ownerUid) throws IllegalStateException {
+        String cmd = String.format("create_asec %s %d %s %s %d",
+                                   id, sizeMb, fstype, key, ownerUid);
+        doCommand(cmd);
+        return getAsecPath(id);
+    }
+
+    public void finalizeAsec(String id) throws IllegalStateException {
+        doCommand("finalize_asec " + id);
+    }
+
+    public void destroyAsec(String id) throws IllegalStateException {
+        doCommand("destroy_asec " + id);
+    }
+
+    public String mountAsec(String id, String key, int ownerUid) throws IllegalStateException {
+        String cmd = String.format("mount_asec %s %s %d",
+                                   id, key, ownerUid);
+        doCommand(cmd);
+        return getAsecPath(id);
+    }
+
+    public String getAsecPath(String id) throws IllegalStateException {
+        ArrayList<String> rsp = doCommand("asec_path " + id);
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code == ResponseCode.AsecPathResult) {
+                return tok[1];
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+        throw new IllegalStateException("Got an empty response");
+    }
+
+    public String[] listAsec() throws IllegalStateException {
+        ArrayList<String> rsp = doCommand("list_asec");
+
+        String[] rdata = new String[rsp.size()];
+        int idx = 0;
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code == ResponseCode.AsecPathResult) {
+                rdata[idx++] = tok[1];
+            } else if (code == ResponseCode.CommandOkay) {
+                return rdata;
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+        throw new IllegalStateException("Got an empty response");
+    }
 }
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 93617d1..1b0b0eb 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -28,7 +28,6 @@
 import android.net.Uri;
 import android.os.IMountService;
 import android.os.Environment;
-import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UEventObserver;
 import android.text.TextUtils;
@@ -36,6 +35,7 @@
 
 import java.io.File;
 import java.io.FileReader;
+import java.lang.IllegalStateException;
 
 /**
  * MountService implements an to the mount service daemon
@@ -136,7 +136,7 @@
     /**
      * @return true if USB mass storage support is enabled.
      */
-    public boolean getMassStorageEnabled() throws RemoteException {
+    public boolean getMassStorageEnabled() {
         return mUmsEnabled;
     }
 
@@ -145,7 +145,7 @@
      * 
      * @param enable  true to enable USB mass storage support
      */
-    public void setMassStorageEnabled(boolean enable) throws RemoteException {
+    public void setMassStorageEnabled(boolean enable) throws IllegalStateException {
         try {
             String vp = Environment.getExternalStorageDirectory().getPath();
             String vs = getVolumeState(vp);
@@ -164,7 +164,7 @@
                 Log.d(TAG, "Mounting media after UMS disable");
                 mountMedia(vp);
             }
-        } catch (RemoteException rex) {
+        } catch (IllegalStateException rex) {
             Log.e(TAG, "Failed to set ums enable {" + enable + "}");
             return;
         }
@@ -173,14 +173,14 @@
     /**
      * @return true if USB mass storage is connected.
      */
-    public boolean getMassStorageConnected() throws RemoteException {
+    public boolean getMassStorageConnected() {
         return mUmsConnected;
     }
 
     /**
      * @return state of the volume at the specified mount point
      */
-    public String getVolumeState(String mountPoint) throws RemoteException {
+    public String getVolumeState(String mountPoint) throws IllegalStateException {
         /*
          * XXX: Until we have multiple volume discovery, just hardwire
          * this to /sdcard
@@ -197,7 +197,7 @@
     /**
      * Attempt to mount external media
      */
-    public void mountMedia(String mountPath) throws RemoteException {
+    public void mountMedia(String mountPath) throws IllegalStateException {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 
                 != PackageManager.PERMISSION_GRANTED) {
@@ -209,7 +209,7 @@
     /**
      * Attempt to unmount external media to prepare for eject
      */
-    public void unmountMedia(String mountPath) throws RemoteException {
+    public void unmountMedia(String mountPath) throws IllegalStateException {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 
                 != PackageManager.PERMISSION_GRANTED) {
@@ -227,7 +227,7 @@
     /**
      * Attempt to format external media
      */
-    public void formatMedia(String formatPath) throws RemoteException {
+    public void formatMedia(String formatPath) throws IllegalStateException {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS) 
                 != PackageManager.PERMISSION_GRANTED) {
@@ -306,7 +306,7 @@
             } else {
                 setUsbStorageNotification(0, 0, 0, false, false, null);
             }
-        } catch (RemoteException e) {
+        } catch (IllegalStateException e) {
             // Nothing to do
         }
     }
@@ -335,7 +335,7 @@
                     } else {
                         Log.d(TAG, "Skipping connection-mount; already mounted");
                     }
-                } catch (RemoteException rex) {
+                } catch (IllegalStateException rex) {
                     Log.e(TAG, "Exception while handling connection mount " + rex);
                 }
 
@@ -350,7 +350,7 @@
     }
 
     void notifyVolumeStateChange(String label, String mountPoint, int oldState,
-                                 int newState) throws RemoteException {
+                                 int newState) throws IllegalStateException {
         String vs = getVolumeState(mountPoint);
 
         if (newState == VolumeState.Init) {
@@ -395,7 +395,7 @@
             if (mAutoStartUms) {
                 try {
                     setMassStorageEnabled(true);
-                } catch (RemoteException e) {
+                } catch (IllegalStateException e) {
                 }
             } else {
                 updateUsbMassStorageNotification(false, true);
@@ -429,7 +429,7 @@
         mContext.sendBroadcast(intent);
     }
 
-    void notifyMediaInserted(final String path) throws RemoteException {
+    void notifyMediaInserted(final String path) throws IllegalStateException {
         new Thread() {
             public void run() {
                 try {
@@ -445,7 +445,7 @@
     /**
      * Broadcasts the media removed event to all clients.
      */
-    void notifyMediaRemoved(String path) throws RemoteException {
+    void notifyMediaRemoved(String path) throws IllegalStateException {
 
         // Suppress this on bad removal
         if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
@@ -755,5 +755,31 @@
             notificationManager.cancel(notificationId);
         }
     }
+
+    public String[] getSecureCacheList() throws IllegalStateException {
+        return mListener.listAsec();
+    }
+
+    public String createSecureCache(String id, int sizeMb, String fstype,
+                                    String key, int ownerUid) throws IllegalStateException {
+        return mListener.createAsec(id, sizeMb, fstype, key, ownerUid);
+    }
+
+    public void finalizeSecureCache(String id) throws IllegalStateException {
+        mListener.finalizeAsec(id);
+    }
+
+    public void destroySecureCache(String id) throws IllegalStateException {
+        mListener.destroyAsec(id);
+    }
+   
+    public String mountSecureCache(String id, String key, int ownerUid) throws IllegalStateException {
+        return mListener.mountAsec(id, key, ownerUid);
+    }
+
+    public String getSecureCachePath(String id) throws IllegalStateException {
+        return mListener.getAsecPath(id);
+    }
+
 }