MountService: Massive bloat reduction and rewrite

- Most API calls now return an int as a result code (see MountServiceResultCode.java)
- All notification code has been removed
- All settings code has been removed
- Removed UMS centric API calls in favor of more generic 'shares'
- Mount error reporting is no longer done via an event, but is done as part of the
  actual mount process
- Rework vold IPC commands to be more sane

Updated:
    MountService: Rename MountServiceObserver -> MountServiceListener
    MountService: Add support for Async callbacks

Updated:
    MountService: Add BinderDeath handling

Updated:
    MountService: Remove notifys since we dont listen anyways

Updated:
    MountService: Fix bad cast

Signed-off-by: San Mehat <san@google.com>
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 05cea46..6382646 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -16,9 +16,6 @@
 
 package com.android.server;
 
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -27,6 +24,10 @@
 import android.content.res.Resources;
 import android.net.Uri;
 import android.os.IMountService;
+import android.os.IMountServiceListener;
+import android.os.MountServiceResultCode;
+import android.os.RemoteException;
+import android.os.IBinder;
 import android.os.Environment;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -36,13 +37,8 @@
 import android.util.Log;
 import java.util.ArrayList;
 
-import android.provider.Settings;
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-
 import java.io.File;
 import java.io.FileReader;
-import java.lang.IllegalStateException;
 
 /**
  * MountService implements an to the mount service daemon
@@ -53,6 +49,9 @@
     
     private static final String TAG = "MountService";
 
+    /*
+     * Internal vold volume state constants
+     */
     class VolumeState {
         public static final int Init       = -1;
         public static final int NoMedia    = 0;
@@ -66,73 +65,50 @@
         public static final int SharedMnt  = 8;
     }
 
+    /*
+     * Internal vold response code constants
+     */
     class VoldResponseCode {
+        /*
+         * 100 series - Requestion action was initiated; expect another reply
+         *              before proceeding with a new command.
+         */
         public static final int VolumeListResult               = 110;
         public static final int AsecListResult                 = 111;
 
-        public static final int ShareAvailabilityResult        = 210;
+        /*
+         * 200 series - Requestion action has been successfully completed.
+         */
+        public static final int ShareStatusResult              = 210;
         public static final int AsecPathResult                 = 211;
+        public static final int ShareEnabledResult             = 212;
 
+        /*
+         * 400 series - Command was accepted, but the requested action
+         *              did not take place.
+         */
+        public static final int OpFailedNoMedia                = 401;
+        public static final int OpFailedMediaBlank             = 402;
+        public static final int OpFailedMediaCorrupt           = 403;
+        public static final int OpFailedVolNotMounted          = 404;
+        public static final int OpFailedVolBusy                = 405;
+
+        /*
+         * 600 series - Unsolicited broadcasts.
+         */
         public static final int VolumeStateChange              = 605;
-        public static final int VolumeMountFailedBlank         = 610;
-        public static final int VolumeMountFailedDamaged       = 611;
-        public static final int VolumeMountFailedNoMedia       = 612;
         public static final int ShareAvailabilityChange        = 620;
         public static final int VolumeDiskInserted             = 630;
         public static final int VolumeDiskRemoved              = 631;
         public static final int VolumeBadRemoval               = 632;
     }
 
-
-    /**
-     * Binder context for this service
-     */
-    private Context mContext;
-    
-    /**
-     * connectorr object for communicating with vold
-     */
-    private NativeDaemonConnector mConnector;
-
-    /**
-     * The notification that is shown when a USB mass storage host
-     * is connected. 
-     * <p>
-     * This is lazily created, so use {@link #setUsbStorageNotification()}.
-     */
-    private Notification mUsbStorageNotification;
-
-
-    /**
-     * The notification that is shown when the following media events occur:
-     *     - Media is being checked
-     *     - Media is blank (or unknown filesystem)
-     *     - Media is corrupt
-     *     - Media is safe to unmount
-     *     - Media is missing
-     * <p>
-     * This is lazily created, so use {@link #setMediaStorageNotification()}.
-     */
-    private Notification mMediaStorageNotification;
-    
-    private boolean mShowSafeUnmountNotificationWhenUnmounted;
-
-    private boolean mPlaySounds;
-
-    private boolean mMounted;
-
-    private SettingsWatcher mSettingsWatcher;
-    private boolean mAutoStartUms;
-    private boolean mPromptUms;
-    private boolean mUmsActiveNotify;
-
-    private boolean mUmsConnected = false;
-    private boolean mUmsEnabled = false;
-    private boolean mUmsEnabling = false;
-
-    private String  mLegacyState = Environment.MEDIA_REMOVED;
-
-    private PackageManagerService mPms;
+    private Context                               mContext;
+    private NativeDaemonConnector                 mConnector;
+    private String                                mLegacyState = Environment.MEDIA_REMOVED;
+    private PackageManagerService                 mPms;
+    private boolean                               mUmsEnabling;
+    private ArrayList<MountServiceBinderListener> mListeners;
 
     /**
      * Constructs a new MountService instance
@@ -142,7 +118,9 @@
     public MountService(Context context) {
         mContext = context;
 
+        // XXX: This will go away soon in favor of IMountServiceObserver
         mPms = (PackageManagerService) ServiceManager.getService("package");
+
         // Register a BOOT_COMPLETED handler so that we can start
         // our NativeDaemonConnector. We defer the startup so that we don't
         // start processing events before we ought-to
@@ -150,78 +128,9 @@
                 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
 
         mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
-        mShowSafeUnmountNotificationWhenUnmounted = false;
-
-        mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
-
-        ContentResolver cr = mContext.getContentResolver();
-        mAutoStartUms = (Settings.Secure.getInt(
-                cr, Settings.Secure.MOUNT_UMS_AUTOSTART, 0) == 1);
-        mPromptUms = (Settings.Secure.getInt(
-                cr, Settings.Secure.MOUNT_UMS_PROMPT, 1) == 1);
-        mUmsActiveNotify = (Settings.Secure.getInt(
-                cr, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, 1) == 1);
-
-        mSettingsWatcher = new SettingsWatcher(new Handler());
+        mListeners = new ArrayList<MountServiceBinderListener>();
     }
   
-    private class SettingsWatcher extends ContentObserver {
-        public SettingsWatcher(Handler handler) {
-            super(handler);
-            ContentResolver cr = mContext.getContentResolver();
-            cr.registerContentObserver(Settings.System.getUriFor(
-                    Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND), false, this);
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.MOUNT_UMS_AUTOSTART), false, this);
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.MOUNT_UMS_PROMPT), false, this);
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED), false, this);
-        }
-
-        public void onChange(boolean selfChange) {
-            super.onChange(selfChange);
-            ContentResolver cr = mContext.getContentResolver();
-
-            boolean newPlayNotificationSounds = (Settings.Secure.getInt(
-                    cr, Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND, 1) == 1);
-
-            boolean newUmsAutostart = (Settings.Secure.getInt(
-                    cr, Settings.Secure.MOUNT_UMS_AUTOSTART, 0) == 1);
-
-            if (newUmsAutostart != mAutoStartUms) {
-                mAutoStartUms = newUmsAutostart;
-            }
-
-            boolean newUmsPrompt = (Settings.Secure.getInt(
-                    cr, Settings.Secure.MOUNT_UMS_PROMPT, 1) == 1);
-
-            if (newUmsPrompt != mPromptUms) {
-                mPromptUms = newUmsAutostart;
-            }
-
-            boolean newUmsNotifyEnabled = (Settings.Secure.getInt(
-                    cr, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, 1) == 1);
-
-            if (mUmsEnabled) {
-                if (newUmsNotifyEnabled) {
-                    Intent intent = new Intent();
-                    intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
-                    PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-                    setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
-                            com.android.internal.R.string.usb_storage_stop_notification_message,
-                            com.android.internal.R.drawable.stat_sys_warning,
-                            false, true, pi);
-                } else {
-                    setUsbStorageNotification(0, 0, 0, false, false, null);
-                }
-            }
-            if (newUmsNotifyEnabled != mUmsActiveNotify) {
-                mUmsActiveNotify = newUmsNotifyEnabled;
-            }
-        }
-    }
-
     BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
@@ -232,8 +141,7 @@
                  * event to trigger MediaScanner
                  */
                 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
-                    notifyMediaMounted(
-                            Environment.getExternalStorageDirectory().getPath(), false);
+                    updatePublicVolumeState("/sdcard", Environment.MEDIA_MOUNTED);
                     return;
                 }
 
@@ -244,15 +152,394 @@
         }
     };
 
-    public void shutdown() {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.SHUTDOWN)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires SHUTDOWN permission");
+    private final class MountServiceBinderListener implements IBinder.DeathRecipient {
+        final IMountServiceListener mListener;
+
+        MountServiceBinderListener(IMountServiceListener listener) {
+            mListener = listener;
+ 
         }
 
-        Log.d(TAG, "Shutting down");
-        String state = Environment.getExternalStorageState();
+        public void binderDied() {
+            Log.d(TAG, "An IMountServiceListener has died!");
+            synchronized(mListeners) {
+                mListeners.remove(this);
+                mListener.asBinder().unlinkToDeath(this, 0);
+            }
+        }
+    }
+
+    int doShareUnshareVolume(String path, String method, boolean enable) {
+        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+
+        // TODO: Add support for multiple share methods
+        if (!method.equals("ums")) {
+            throw new IllegalArgumentException(String.format("Method %s not supported", method));
+        }
+
+        /*
+         * If the volume is mounted and we're enabling then unmount it
+         */
+        String vs = getVolumeState(path);
+        if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
+            mUmsEnabling = enable; // Supress unmounted events
+            unmountVolume(path);
+            mUmsEnabling = false; // Unsupress unmounted events
+        }
+
+        try {
+            mConnector.doCommand(String.format(
+                    "volume %sshare %s %s", (enable ? "" : "un"), path, method));
+        } catch (NativeDaemonConnectorException e) {
+            Log.e(TAG, "Failed to share/unshare", e);
+            return MountServiceResultCode.OperationFailedInternalError;
+        }
+
+        /*
+         * If we disabled UMS then mount the volume
+         */
+        if (!enable) {
+            if (mountVolume(path) != MountServiceResultCode.OperationSucceeded) {
+                Log.e(TAG, String.format(
+                        "Failed to remount %s after disabling share method %s", path, method));
+                /*
+                 * Even though the mount failed, the unshare didn't so don't indicate an error.
+                 * The mountVolume() call will have set the storage state and sent the necessary
+                 * broadcasts.
+                 */
+            }
+        }
+
+        return MountServiceResultCode.OperationSucceeded;
+    }
+
+    void updatePublicVolumeState(String path, String state) {
+        if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
+            Log.w(TAG, "Multiple volumes not currently supported");
+            return;
+        }
+        Log.i(TAG, "State for {" + path + "} = {" + state + "}");
+
+        String oldState = mLegacyState;
+        mLegacyState = state;
+
+        synchronized (mListeners) {
+            for (int i = mListeners.size() -1; i >= 0; i--) {
+                MountServiceBinderListener bl = mListeners.get(i);
+                try {
+                    bl.mListener.onVolumeStateChanged("", path, oldState, state);
+                } catch (RemoteException rex) {
+                    Log.e(TAG, "Listener dead");
+                    mListeners.remove(i);
+                } catch (Exception ex) {
+                    Log.e(TAG, "Listener failed", ex);
+                }
+            }
+        }
+    }
+
+    /**
+     *
+     * Callback from NativeDaemonConnector
+     */
+    public void onDaemonConnected() {
+        /*
+         * Since we'll be calling back into the NativeDaemonConnector,
+         * we need to do our work in a new thread.
+         */
+        new Thread() {
+            public void run() {
+                /**
+                 * Determine media state and UMS detection status
+                 */
+                String path = Environment.getExternalStorageDirectory().getPath();
+                String state = Environment.MEDIA_REMOVED;
+
+                try {
+                    String[] vols = mConnector.doListCommand(
+                        "volume list", VoldResponseCode.VolumeListResult);
+                    for (String volstr : vols) {
+                        String[] tok = volstr.split(" ");
+                        // FMT: <label> <mountpoint> <state>
+                        if (!tok[1].equals(path)) {
+                            Log.w(TAG, String.format(
+                                    "Skipping unknown volume '%s'",tok[1]));
+                            continue;
+                        }
+                        int st = Integer.parseInt(tok[2]);
+                        if (st == VolumeState.NoMedia) {
+                            state = Environment.MEDIA_REMOVED;
+                        } else if (st == VolumeState.Idle) {
+                            state = null;
+                            int rc = mountVolume(path);
+                            if (rc != MountServiceResultCode.OperationSucceeded) {
+                                Log.e(TAG, String.format("Connection-mount failed (%d)", rc));
+                            }
+                        } else if (st == VolumeState.Mounted) {
+                            state = Environment.MEDIA_MOUNTED;
+                            Log.i(TAG, "Media already mounted on daemon connection");
+                        } else if (st == VolumeState.Shared) {
+                            state = Environment.MEDIA_SHARED;
+                            Log.i(TAG, "Media shared on daemon connection");
+                        } else {
+                            throw new Exception(String.format("Unexpected state %d", st));
+                        }
+                    }
+                    if (state != null) {
+                        updatePublicVolumeState(path, state);
+                    }
+                } catch (Exception e) {
+                    Log.e(TAG, "Error processing initial volume state", e);
+                    updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
+                }
+
+                try {
+                    boolean avail = getShareMethodAvailable("ums");
+                    notifyShareAvailabilityChange("ums", avail);
+                } catch (Exception ex) {
+                    Log.w(TAG, "Failed to get share availability");
+                }
+            }
+        }.start();
+    }
+
+    /**
+     *
+     * Callback from NativeDaemonConnector
+     */
+    public boolean onEvent(int code, String raw, String[] cooked) {
+        Intent in = null;
+
+        // Log.d(TAG, "event {" + raw + "}");
+        if (code == VoldResponseCode.VolumeStateChange) {
+            /*
+             * One of the volumes we're managing has changed state.
+             * Format: "NNN Volume <label> <path> state changed
+             * from <old_#> (<old_str>) to <new_#> (<new_str>)"
+             */
+            notifyVolumeStateChange(
+                    cooked[2], cooked[3], Integer.parseInt(cooked[7]),
+                            Integer.parseInt(cooked[10]));
+        } else if (code == VoldResponseCode.ShareAvailabilityChange) {
+            // FMT: NNN Share method <method> now <available|unavailable>
+            boolean avail = false;
+            if (cooked[5].equals("available")) {
+                avail = true;
+            }
+            notifyShareAvailabilityChange(cooked[3], avail);
+        } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
+                   (code == VoldResponseCode.VolumeDiskRemoved) ||
+                   (code == VoldResponseCode.VolumeBadRemoval)) {
+            // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
+            // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
+            // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
+            final String label = cooked[2];
+            final String path = cooked[3];
+            int major = -1;
+            int minor = -1;
+
+            try {
+                String devComp = cooked[6].substring(1, cooked[6].length() -1);
+                String[] devTok = devComp.split(":");
+                major = Integer.parseInt(devTok[0]);
+                minor = Integer.parseInt(devTok[1]);
+            } catch (Exception ex) {
+                Log.e(TAG, "Failed to parse major/minor", ex);
+            }
+
+            synchronized (mListeners) {
+                for (int i = mListeners.size() -1; i >= 0; i--) {
+                    MountServiceBinderListener bl = mListeners.get(i);
+                    try {
+                        if (code == VoldResponseCode.VolumeDiskInserted) {
+                            bl.mListener.onMediaInserted(label, path, major, minor);
+                        } else if (code == VoldResponseCode.VolumeDiskRemoved) {
+                            bl.mListener.onMediaRemoved(label, path, major, minor, true);
+                        } else if (code == VoldResponseCode.VolumeBadRemoval) {
+                            bl.mListener.onMediaRemoved(label, path, major, minor, false);
+                        } else {
+                            Log.e(TAG, String.format("Unknown code {%d}", code));
+                        }
+                    } catch (RemoteException rex) {
+                        Log.e(TAG, "Listener dead");
+                        mListeners.remove(i);
+                    } catch (Exception ex) {
+                        Log.e(TAG, "Listener failed", ex);
+                    }
+                }
+            }
+
+            if (code == VoldResponseCode.VolumeDiskInserted) {
+                new Thread() {
+                    public void run() {
+                        try {
+                            int rc;
+                            if ((rc = mountVolume(path)) != MountServiceResultCode.OperationSucceeded) {
+                                Log.w(TAG, String.format("Insertion mount failed (%d)", rc));
+                            }
+                        } catch (Exception ex) {
+                            Log.w(TAG, "Failed to mount media on insertion", ex);
+                        }
+                    }
+                }.start();
+            } else if (code == VoldResponseCode.VolumeDiskRemoved) {
+                /*
+                 * This event gets trumped if we're already in BAD_REMOVAL state
+                 */
+                if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
+                    return true;
+                }
+                /* Send the media unmounted event first */
+                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
+                mContext.sendBroadcast(in);
+
+                updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
+                in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path));
+            } else if (code == VoldResponseCode.VolumeBadRemoval) {
+                /* Send the media unmounted event first */
+                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
+                mContext.sendBroadcast(in);
+
+                updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
+                in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path));
+            } else {
+                Log.e(TAG, String.format("Unknown code {%d}", code));
+            }
+        } else {
+            return false;
+        }
+
+        if (in != null) {
+            mContext.sendBroadcast(in);
+	}
+       return true;
+    }
+
+    void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
+        String vs = getVolumeState(path);
+
+        Intent in = null;
+
+        if (newState == VolumeState.Init) {
+        } else if (newState == VolumeState.NoMedia) {
+            // NoMedia is handled via Disk Remove events
+        } else if (newState == VolumeState.Idle) {
+            /*
+             * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
+             * if we're in the process of enabling UMS
+             */
+            if (!vs.equals(
+                    Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
+                            Environment.MEDIA_NOFS) && !vs.equals(
+                                    Environment.MEDIA_UNMOUNTABLE) && !mUmsEnabling) {
+                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
+            }
+        } else if (newState == VolumeState.Pending) {
+        } else if (newState == VolumeState.Checking) {
+            updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
+            in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path));
+        } else if (newState == VolumeState.Mounted) {
+            updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
+            // Update media status on PackageManagerService to mount packages on sdcard
+            mPms.updateExternalMediaStatus(true);
+            in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
+            in.putExtra("read-only", false);
+        } else if (newState == VolumeState.Unmounting) {
+            mPms.updateExternalMediaStatus(false);
+            in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
+        } else if (newState == VolumeState.Formatting) {
+        } else if (newState == VolumeState.Shared) {
+            /* Send the media unmounted event first */
+            updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+            in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
+            mContext.sendBroadcast(in);
+
+            updatePublicVolumeState(path, Environment.MEDIA_SHARED);
+            in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
+        } else if (newState == VolumeState.SharedMnt) {
+            Log.e(TAG, "Live shared mounts not supported yet!");
+            return;
+        } else {
+            Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
+        }
+
+        if (in != null) {
+            mContext.sendBroadcast(in);
+        }
+    }
+
+    void notifyShareAvailabilityChange(String method, final boolean avail) {
+        if (!method.equals("ums")) {
+           Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
+           return;
+        }
+
+        synchronized (mListeners) {
+            for (int i = mListeners.size() -1; i >= 0; i--) {
+                MountServiceBinderListener bl = mListeners.get(i);
+                try {
+                    bl.mListener.onShareAvailabilityChanged(method, avail);
+                } catch (RemoteException rex) {
+                    Log.e(TAG, "Listener dead");
+                    mListeners.remove(i);
+                } catch (Exception ex) {
+                    Log.e(TAG, "Listener failed", ex);
+                }
+            }
+        }
+
+        Intent intent;
+        if (avail) {
+            intent = new Intent(Intent.ACTION_UMS_CONNECTED);
+        } else {
+            intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
+        }
+        mContext.sendBroadcast(intent);
+    }
+
+    void validatePermission(String perm) {
+        if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(String.format("Requires %s permission", perm));
+        }
+    }
+
+    /**
+     * Exposed API calls below here
+     */
+
+    public void registerListener(IMountServiceListener listener) {
+        synchronized (mListeners) {
+            MountServiceBinderListener bl = new MountServiceBinderListener(listener);
+            try {
+                listener.asBinder().linkToDeath(bl, 0);
+                mListeners.add(bl);
+            } catch (RemoteException rex) {
+                Log.e(TAG, "Failed to link to listener death");
+            }
+        }
+    }
+
+    public void unregisterListener(IMountServiceListener listener) {
+        synchronized (mListeners) {
+            for(MountServiceBinderListener bl : mListeners) {
+                if (bl.mListener == listener) {
+                    mListeners.remove(mListeners.indexOf(bl));
+                    return;
+                }
+            }
+        }
+    }
+
+    public void shutdown() {
+        validatePermission(android.Manifest.permission.SHUTDOWN);
+
+        Log.i(TAG, "Shutting down");
+
+        String path = Environment.getExternalStorageDirectory().getPath();
+        String state = getVolumeState(path);
 
         if (state.equals(Environment.MEDIA_SHARED)) {
             /*
@@ -262,10 +549,8 @@
              * the UMS host could have dirty FAT cache entries
              * yet to flush.
              */
-            try {
-               setMassStorageEnabled(false);
-            } catch (Exception e) {
-                Log.e(TAG, "ums disable failed", e);
+            if (unshareVolume(path, "ums") != MountServiceResultCode.OperationSucceeded) {
+                Log.e(TAG, "UMS disable on shutdown failed");
             }
         } else if (state.equals(Environment.MEDIA_CHECKING)) {
             /*
@@ -292,86 +577,81 @@
             /*
              * If the media is mounted, then gracefully unmount it.
              */
+            if (unmountVolume(path) != MountServiceResultCode.OperationSucceeded) {
+                Log.e(TAG, "Failed to unmount media for shutdown");
+            }
+        }
+    }
+
+    public String[] getShareMethodList() {
+        String[] rdata = new String[1];
+        rdata[0] = "ums";
+        return rdata;
+    }
+
+    public boolean getShareMethodAvailable(String method) {
+        ArrayList<String> rsp = mConnector.doCommand("share status " + method);
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code;
             try {
-                String m = Environment.getExternalStorageDirectory().toString();
-                unmountVolume(m);
-
-                int retries = 12;
-                while (!state.equals(Environment.MEDIA_UNMOUNTED) && (retries-- >=0)) {
-                    try {
-                        Thread.sleep(1000);
-                    } catch (InterruptedException iex) {
-                        Log.e(TAG, "Interrupted while waiting for media", iex);
-                        break;
-                    }
-                    state = Environment.getExternalStorageState();
-                }
-                if (retries == 0) {
-                    Log.e(TAG, "Timed out waiting for media to unmount");
-                }
-            } catch (Exception e) {
-                Log.e(TAG, "external storage unmount failed", e);
+                code = Integer.parseInt(tok[0]);
+            } catch (NumberFormatException nfe) {
+                Log.e(TAG, String.format("Error parsing code %s", tok[0]));
+                return false;
+            }
+            if (code == VoldResponseCode.ShareStatusResult) {
+                if (tok[2].equals("available"))
+                    return true;
+                return false;
+            } else {
+                Log.e(TAG, String.format("Unexpected response code %d", code));
+                return false;
             }
         }
+        Log.e(TAG, "Got an empty response");
+        return false;
     }
 
-    /**
-     * @return true if USB mass storage support is enabled.
-     */
-    public boolean getMassStorageEnabled() {
-        return mUmsEnabled;
+    public int shareVolume(String path, String method) {
+        return doShareUnshareVolume(path, method, true);
     }
 
-    /**
-     * Enables or disables USB mass storage support.
-     * 
-     * @param enable  true to enable USB mass storage support
-     */
-    public void setMassStorageEnabled(boolean enable) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
-        }
-        try {
-            String vp = Environment.getExternalStorageDirectory().getPath();
-            String vs = getVolumeState(vp);
+    public int unshareVolume(String path, String method) {
+        return doShareUnshareVolume(path, method, false);
+    }
 
-            mUmsEnabling = enable;
-            if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
-                unmountVolume(vp);
-                mUmsEnabling = false;
-                updateUsbMassStorageNotification(true, false);
+    public boolean getVolumeShared(String path, String method) {
+        String cmd = String.format("volume shared %s %s", path, method);
+        ArrayList<String> rsp = mConnector.doCommand(cmd);
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code;
+            try {
+                code = Integer.parseInt(tok[0]);
+            } catch (NumberFormatException nfe) {
+                Log.e(TAG, String.format("Error parsing code %s", tok[0]));
+                return false;
             }
-
-            setShareMethodEnabled(vp, "ums", enable);
-            mUmsEnabled = enable;
-            mUmsEnabling = false;
-            if (!enable) {
-                mountVolume(vp);
-                if (mPromptUms) {
-                    updateUsbMassStorageNotification(false, false);
-                } else {
-                    updateUsbMassStorageNotification(true, false);
-                }
+            if (code == VoldResponseCode.ShareEnabledResult) {
+                if (tok[2].equals("enabled"))
+                    return true;
+                return false;
+            } else {
+                Log.e(TAG, String.format("Unexpected response code %d", code));
+                return false;
             }
-        } catch (IllegalStateException rex) {
-            Log.e(TAG, "Failed to set ums enable {" + enable + "}");
-            return;
         }
+        Log.e(TAG, "Got an empty response");
+        return false;
     }
-
-    /**
-     * @return true if USB mass storage is connected.
-     */
-    public boolean getMassStorageConnected() {
-        return mUmsConnected;
-    }
-
+    
     /**
      * @return state of the volume at the specified mount point
      */
-    public String getVolumeState(String mountPoint) throws IllegalStateException {
+    public String getVolumeState(String mountPoint) {
         /*
          * XXX: Until we have multiple volume discovery, just hardwire
          * this to /sdcard
@@ -388,755 +668,186 @@
     /**
      * Attempt to mount external media
      */
-    public void mountVolume(String mountPath) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
+    public int mountVolume(String path) {
+        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+
+        int rc = MountServiceResultCode.OperationSucceeded;
+
+        try {
+            mConnector.doCommand(String.format("volume mount %s", path));
+        } catch (NativeDaemonConnectorException e) {
+            /*
+             * Mount failed for some reason
+             */
+            Intent in = null;
+            int code = e.getCode();
+            if (code == VoldResponseCode.OpFailedNoMedia) {
+                /*
+                 * Attempt to mount but no media inserted
+                 */
+                rc = MountServiceResultCode.OperationFailedNoMedia;
+            } else if (code == VoldResponseCode.OpFailedMediaBlank) {
+                /*
+                 * Media is blank or does not contain a supported filesystem
+                 */
+                updatePublicVolumeState(path, Environment.MEDIA_NOFS);
+                in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
+                rc = MountServiceResultCode.OperationFailedMediaBlank;
+            } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
+                /*
+                 * Volume consistency check failed
+                 */
+                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
+                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
+                rc = MountServiceResultCode.OperationFailedMediaCorrupt;
+            } else {
+                rc = MountServiceResultCode.OperationFailedInternalError;
+            }
+
+            /*
+             * Send broadcast intent (if required for the failure)
+             */
+            if (in != null) {
+                mContext.sendBroadcast(in);
+            }
         }
-        mConnector.doCommand(String.format("mount %s", mountPath));
+
+        return rc;
     }
 
     /**
      * Attempt to unmount external media to prepare for eject
      */
-    public void unmountVolume(String mountPath) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
-        }
-
-        // Set a flag so that when we get the unmounted event, we know
-        // to display the notification
-        mShowSafeUnmountNotificationWhenUnmounted = true;
-
-        mConnector.doCommand(String.format("unmount %s", mountPath));
-    }
-
-    /**
-     * Attempt to format external media
-     */
-    public void formatVolume(String formatPath) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS) 
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
-        }
-
-        mConnector.doCommand(String.format("format %s", formatPath));
-    }
-
-    boolean getShareAvailable(String method) throws IllegalStateException  {
-        ArrayList<String> rsp = mConnector.doCommand("share_available " + method);
-
-        for (String line : rsp) {
-            String []tok = line.split(" ");
-            int code = Integer.parseInt(tok[0]);
-            if (code == VoldResponseCode.ShareAvailabilityResult) {
-                if (tok[2].equals("available"))
-                    return true;
-                return false;
-            } else {
-                throw new IllegalStateException(String.format("Unexpected response code %d", code));
-            }
-        }
-        throw new IllegalStateException("Got an empty response");
-    }
-
-    /**
-     * Enables or disables USB mass storage support.
-     * 
-     * @param enable  true to enable USB mass storage support
-     */
-    void setShareMethodEnabled(String mountPoint, String method,
-                               boolean enable) throws IllegalStateException {
-        mConnector.doCommand(String.format(
-                "%sshare %s %s", (enable ? "" : "un"), mountPoint, method));
-    }
-
-
-    /**
-     * Returns true if we're playing media notification sounds.
-     */
-    public boolean getPlayNotificationSounds() {
-        return mPlaySounds;
-    }
-
-    /**
-     * Set whether or not we're playing media notification sounds.
-     */
-    public void setPlayNotificationSounds(boolean enabled) {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.WRITE_SETTINGS) 
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires WRITE_SETTINGS permission");
-        }
-        mPlaySounds = enabled;
-        SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0"));
-    }
-
-    void updatePublicVolumeState(String mountPoint, String state) {
-        if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
-            Log.w(TAG, "Multiple volumes not currently supported");
-            return;
-        }
-        Log.i(TAG, "State for {" + mountPoint + "} = {" + state + "}");
-        mLegacyState = state;
-    }
-
-    /**
-     * Update the state of the USB mass storage notification
-     */
-    void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) {
+    public int unmountVolume(String path) {
+        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
         try {
-
-            if (getMassStorageConnected() && !suppressIfConnected) {
-                Intent intent = new Intent();
-                intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-                setUsbStorageNotification(
-                        com.android.internal.R.string.usb_storage_notification_title,
-                        com.android.internal.R.string.usb_storage_notification_message,
-                        com.android.internal.R.drawable.stat_sys_data_usb,
-                        sound, true, pi);
+            mConnector.doCommand(String.format("volume unmount %s", path));
+            return MountServiceResultCode.OperationSucceeded;
+        } catch (NativeDaemonConnectorException e) {
+            int code = e.getCode();
+            if (code == VoldResponseCode.OpFailedVolNotMounted) {
+                return MountServiceResultCode.OperationFailedVolumeNotMounted;
             } else {
-                setUsbStorageNotification(0, 0, 0, false, false, null);
+                return MountServiceResultCode.OperationFailedInternalError;
             }
-        } catch (IllegalStateException e) {
-            // Nothing to do
-        }
-    }
-
-    void handlePossibleExplicitUnmountBroadcast(String path) {
-        if (mMounted) {
-            mMounted = false;
-            // Update media status on PackageManagerService to unmount packages on sdcard
-            mPms.updateExternalMediaStatus(false);
-            Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 
-                    Uri.parse("file://" + path));
-            mContext.sendBroadcast(intent);
         }
     }
 
     /**
+     * Synchronously formats a volume
      *
-     * Callback from NativeDaemonConnector
+     * @param path The volume path to format
+     * @return Error code from MountServiceResultCode
      */
-    public void onDaemonConnected() {
-        /*
-         * Since we'll be calling back into the NativeDaemonConnector,
-         * we need to do our work in a new thread.
-         */
-        new Thread() {
-            public void run() {
-                /**
-                 * Determine media state and UMS detection status
-                 */
-                String path = Environment.getExternalStorageDirectory().getPath();
-                String state = Environment.MEDIA_REMOVED;
+    public int formatVolume(String path) {
+        validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
 
-                try {
-                    String[] vols = mConnector.doListCommand(
-                        "list_volumes", VoldResponseCode.VolumeListResult);
-                    for (String volstr : vols) {
-                        String[] tok = volstr.split(" ");
-                        // FMT: <label> <mountpoint> <state>
-                        if (!tok[1].equals(path)) {
-                            Log.w(TAG, String.format(
-                                    "Skipping unknown volume '%s'",tok[1]));
-                            continue;
-                        }
-                        int st = Integer.parseInt(tok[2]);
-                        if (st == VolumeState.NoMedia) {
-                            state = Environment.MEDIA_REMOVED;
-                        } else if (st == VolumeState.Idle) {
-                            state = null;
-                            try {
-                                mountVolume(path);
-                            } catch (Exception ex) {
-                                Log.e(TAG, "Connection-mount failed", ex);
-                            }
-                        } else if (st == VolumeState.Mounted) {
-                            state = Environment.MEDIA_MOUNTED;
-                            Log.i(TAG, "Media already mounted on daemon connection");
-                        } else if (st == VolumeState.Shared) {
-                            state = Environment.MEDIA_SHARED;
-                            Log.i(TAG, "Media shared on daemon connection");
-                        } else {
-                            throw new Exception(String.format("Unexpected state %d", st));
-                        }
-                    }
-                    if (state != null) {
-                        updatePublicVolumeState(path, state);
-                    }
-                } catch (Exception e) {
-                    Log.e(TAG, "Error processing initial volume state", e);
-                    updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
-                }
-
-                try {
-                    boolean avail = getShareAvailable("ums");
-                    notifyShareAvailabilityChange("ums", avail);
-                } catch (Exception ex) {
-                    Log.w(TAG, "Failed to get share availability");
-                }
-            }
-        }.start();
-    }
-
-    /**
-     *
-     * Callback from NativeDaemonConnector
-     */
-    public boolean onEvent(int code, String raw, String[] cooked) {
-        // Log.d(TAG, "event {" + raw + "}");
-        if (code == VoldResponseCode.VolumeStateChange) {
-            // FMT: NNN Volume <label> <mountpoint> state changed
-            // from <old_#> (<old_str>) to <new_#> (<new_str>)
-            notifyVolumeStateChange(
-                    cooked[2], cooked[3], Integer.parseInt(cooked[7]),
-                            Integer.parseInt(cooked[10]));
-        } else if (code == VoldResponseCode.VolumeMountFailedBlank) {
-            // FMT: NNN Volume <label> <mountpoint> mount failed - no supported file-systems
-            notifyMediaNoFs(cooked[3]);
-            // FMT: NNN Volume <label> <mountpoint> mount failed - no media
-        } else if (code == VoldResponseCode.VolumeMountFailedNoMedia) {
-            notifyMediaRemoved(cooked[3]);
-        } else if (code == VoldResponseCode.VolumeMountFailedDamaged) {
-            // FMT: NNN Volume <label> <mountpoint> mount failed - filesystem check failed
-            notifyMediaUnmountable(cooked[3]);
-        } else if (code == VoldResponseCode.ShareAvailabilityChange) {
-            // FMT: NNN Share method <method> now <available|unavailable>
-            boolean avail = false;
-            if (cooked[5].equals("available")) {
-                avail = true;
-            }
-            notifyShareAvailabilityChange(cooked[3], avail);
-        } else if (code == VoldResponseCode.VolumeDiskInserted) {
-            // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
-            notifyMediaInserted(cooked[3]);
-        } else if (code == VoldResponseCode.VolumeDiskRemoved) {
-            // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
-            notifyMediaRemoved(cooked[3]);
-        } else if (code == VoldResponseCode.VolumeBadRemoval) {
-            // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
-            notifyMediaBadRemoval(cooked[3]);
-        } else {
-            return false;
-        }
-       return true;
-    }
-
-    void notifyVolumeStateChange(String label, String mountPoint, int oldState,
-                                 int newState) throws IllegalStateException {
-        String vs = getVolumeState(mountPoint);
-
-        if (newState == VolumeState.Init) {
-        } else if (newState == VolumeState.NoMedia) {
-            // NoMedia is handled via Disk Remove events
-        } else if (newState == VolumeState.Idle) {
-            /*
-             * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
-             * if we're in the process of enabling UMS
-             */
-            if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) &&
-                !vs.equals(Environment.MEDIA_NOFS) &&
-                !vs.equals(Environment.MEDIA_UNMOUNTABLE) &&
-                !mUmsEnabling) {
-                notifyMediaUnmounted(mountPoint);
-            }
-        } else if (newState == VolumeState.Pending) {
-        } else if (newState == VolumeState.Checking) {
-            notifyMediaChecking(mountPoint);
-        } else if (newState == VolumeState.Mounted) {
-            notifyMediaMounted(mountPoint, false);
-        } else if (newState == VolumeState.Unmounting) {
-            notifyMediaUnmounting(mountPoint);
-        } else if (newState == VolumeState.Formatting) {
-        } else if (newState == VolumeState.Shared) {
-            notifyMediaShared(mountPoint, false);
-        } else if (newState == VolumeState.SharedMnt) {
-            notifyMediaShared(mountPoint, true);
-        } else {
-            Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
-        }
-    }
-
-
-    /**
-     * Broadcasts the USB mass storage connected event to all clients.
-     */
-    void notifyUmsConnected() {
-        mUmsConnected = true;
-
-        String storageState = Environment.getExternalStorageState();
-        if (!storageState.equals(Environment.MEDIA_REMOVED) &&
-            !storageState.equals(Environment.MEDIA_BAD_REMOVAL) &&
-            !storageState.equals(Environment.MEDIA_CHECKING)) {
-
-            if (mAutoStartUms) {
-                try {
-                    setMassStorageEnabled(true);
-                } catch (IllegalStateException e) {
-                }
-            } else if (mPromptUms) {
-                updateUsbMassStorageNotification(false, true);
-            }
-        }
-
-        Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
-        mContext.sendBroadcast(intent);
-    }
-
-    void notifyShareAvailabilityChange(String method, final boolean avail) {
-        if (!method.equals("ums")) {
-           Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
-           return;
-        }
-
-        /*
-         * Notification needs to run in a different thread as
-         * it may need to call back into vold
-         */
-        new Thread() {
-            public void run() {
-                try {
-                    if (avail) {
-                        notifyUmsConnected();
-                    } else {
-                        notifyUmsDisconnected();
-                    }
-                } catch (Exception ex) {
-                    Log.w(TAG, "Failed to mount media on insertion");
-                }
-            }
-        }.start();
-    }
-
-    /**
-     * Broadcasts the USB mass storage disconnected event to all clients.
-     */
-    void notifyUmsDisconnected() {
-        mUmsConnected = false;
-        if (mUmsEnabled) {
-            try {
-                Log.w(TAG, "UMS disconnected while enabled!");
-                setMassStorageEnabled(false);
-            } catch (Exception ex) {
-                Log.e(TAG, "Error disabling UMS on unsafe UMS disconnect", ex);
-            }
-        }
-        updateUsbMassStorageNotification(false, false);
-        Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
-        mContext.sendBroadcast(intent);
-    }
-
-    void notifyMediaInserted(final String path) throws IllegalStateException {
-        new Thread() {
-            public void run() {
-                try {
-                    mountVolume(path);
-                } catch (Exception ex) {
-                    Log.w(TAG, "Failed to mount media on insertion", ex);
-                }
-            }
-        }.start();
-    }
-
-    /**
-     * Broadcasts the media removed event to all clients.
-     */
-    void notifyMediaRemoved(String path) throws IllegalStateException {
-
-        // Suppress this on bad removal
-        if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
-            return;
-        }
-
-        updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
-
-        updateUsbMassStorageNotification(true, false);
-
-        setMediaStorageNotification(
-            com.android.internal.R.string.ext_media_nomedia_notification_title,
-            com.android.internal.R.string.ext_media_nomedia_notification_message,
-            com.android.internal.R.drawable.stat_notify_sdcard_usb,
-            true, false, null);
-        handlePossibleExplicitUnmountBroadcast(path);
-
-        Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media unmounted event to all clients.
-     */
-    void notifyMediaUnmounted(String path) {
-
-        updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
-
-        // Update media status on PackageManagerService to unmount packages on sdcard
-        mPms.updateExternalMediaStatus(false);
-        if (mShowSafeUnmountNotificationWhenUnmounted) {
-            setMediaStorageNotification(
-                    com.android.internal.R.string.ext_media_safe_unmount_notification_title,
-                    com.android.internal.R.string.ext_media_safe_unmount_notification_message,
-                    com.android.internal.R.drawable.stat_notify_sdcard,
-                    true, true, null);
-            mShowSafeUnmountNotificationWhenUnmounted = false;
-        } else {
-            setMediaStorageNotification(0, 0, 0, false, false, null);
-        }
-        updateUsbMassStorageNotification(false, false);
-
-        Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media checking event to all clients.
-     */
-    void notifyMediaChecking(String path) {
-        updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
-
-        setMediaStorageNotification(
-                com.android.internal.R.string.ext_media_checking_notification_title,
-                com.android.internal.R.string.ext_media_checking_notification_message,
-                com.android.internal.R.drawable.stat_notify_sdcard_prepare,
-                true, false, null);
-
-        updateUsbMassStorageNotification(true, false);
-        Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media nofs event to all clients.
-     */
-    void notifyMediaNoFs(String path) {
-        updatePublicVolumeState(path, Environment.MEDIA_NOFS);
-        
-        Intent intent = new Intent();
-        intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
-        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-
-        setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title,
-                                    com.android.internal.R.string.ext_media_nofs_notification_message,
-                                    com.android.internal.R.drawable.stat_notify_sdcard_usb,
-                                    true, false, pi);
-        updateUsbMassStorageNotification(false, false);
-        intent = new Intent(Intent.ACTION_MEDIA_NOFS, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media mounted event to all clients.
-     */
-    void notifyMediaMounted(String path, boolean readOnly) {
-        updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
-
-        // Update media status on PackageManagerService to mount packages on sdcard
-        mPms.updateExternalMediaStatus(true);
-        setMediaStorageNotification(0, 0, 0, false, false, null);
-        updateUsbMassStorageNotification(false, false);
-        Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, 
-                Uri.parse("file://" + path));
-        intent.putExtra("read-only", readOnly);
-        mMounted = true;
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media shared event to all clients.
-     */
-    void notifyMediaShared(String path, boolean mounted) {
-        if (mounted) {
-            Log.e(TAG, "Live shared mounts not supported yet!");
-            return;
-        }
-
-        updatePublicVolumeState(path, Environment.MEDIA_SHARED);
-
-        if (mUmsActiveNotify) {
-            Intent intent = new Intent();
-            intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
-            PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-            setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
-                    com.android.internal.R.string.usb_storage_stop_notification_message,
-                    com.android.internal.R.drawable.stat_sys_warning,
-                    false, true, pi);
-        }
-        handlePossibleExplicitUnmountBroadcast(path);
-        Intent intent = new Intent(Intent.ACTION_MEDIA_SHARED,
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media bad removal event to all clients.
-     */
-    void notifyMediaBadRemoval(String path) {
-        updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
-
-        updateUsbMassStorageNotification(true, false);
-        setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title,
-                                    com.android.internal.R.string.ext_media_badremoval_notification_message,
-                                    com.android.internal.R.drawable.stat_sys_warning,
-                                    true, true, null);
-
-        handlePossibleExplicitUnmountBroadcast(path);
-        Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Broadcasts the media unmountable event to all clients.
-     */
-    void notifyMediaUnmountable(String path) {
-        updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
-
-        Intent intent = new Intent();
-        intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
-        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-
-        setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title,
-                                    com.android.internal.R.string.ext_media_unmountable_notification_message,
-                                    com.android.internal.R.drawable.stat_notify_sdcard_usb,
-                                    true, false, pi); 
-        updateUsbMassStorageNotification(false, false);
-
-        handlePossibleExplicitUnmountBroadcast(path);
-
-        intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-    
-    /**
-     * Broadcasts the media eject event to all clients.
-     */
-    void notifyMediaUnmounting(String path) {
-        Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT, 
-                Uri.parse("file://" + path));
-        mContext.sendBroadcast(intent);
-    }
-    
-    /**
-     * Sets the USB storage notification.
-     */
-    private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible,
-                                                        PendingIntent pi) {
-
-        if (!visible && mUsbStorageNotification == null) {
-            return;
-        }
-
-        NotificationManager notificationManager = (NotificationManager) mContext
-                .getSystemService(Context.NOTIFICATION_SERVICE);
-
-        if (notificationManager == null) {
-            return;
-        }
-        
-        if (visible) {
-            Resources r = Resources.getSystem();
-            CharSequence title = r.getText(titleId);
-            CharSequence message = r.getText(messageId);
-
-            if (mUsbStorageNotification == null) {
-                mUsbStorageNotification = new Notification();
-                mUsbStorageNotification.icon = icon;
-                mUsbStorageNotification.when = 0;
-            }
-
-            if (sound && mPlaySounds) {
-                mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
+        try {
+            String cmd = String.format("volume format %s", path);
+            mConnector.doCommand(cmd);
+            return MountServiceResultCode.OperationSucceeded;
+        } catch (NativeDaemonConnectorException e) {
+            int code = e.getCode();
+            if (code == VoldResponseCode.OpFailedNoMedia) {
+                return MountServiceResultCode.OperationFailedNoMedia;
+            } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
+                return MountServiceResultCode.OperationFailedMediaCorrupt;
             } else {
-                mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
+                return MountServiceResultCode.OperationFailedInternalError;
             }
-                
-            mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
-
-            mUsbStorageNotification.tickerText = title;
-            if (pi == null) {
-                Intent intent = new Intent();
-                pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
-            }
-
-            mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
-        }
-    
-        final int notificationId = mUsbStorageNotification.icon;
-        if (visible) {
-            notificationManager.notify(notificationId, mUsbStorageNotification);
-        } else {
-            notificationManager.cancel(notificationId);
         }
     }
 
-    private synchronized boolean getMediaStorageNotificationDismissable() {
-        if ((mMediaStorageNotification != null) &&
-            ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
-                    Notification.FLAG_AUTO_CANCEL))
-            return true;
-
-        return false;
-    }
-
-    /**
-     * Sets the media storage notification.
-     */
-    private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
-                                                          boolean dismissable, PendingIntent pi) {
-
-        if (!visible && mMediaStorageNotification == null) {
-            return;
-        }
-
-        NotificationManager notificationManager = (NotificationManager) mContext
-                .getSystemService(Context.NOTIFICATION_SERVICE);
-
-        if (notificationManager == null) {
-            return;
-        }
-
-        if (mMediaStorageNotification != null && visible) {
-            /*
-             * Dismiss the previous notification - we're about to
-             * re-use it.
-             */
-            final int notificationId = mMediaStorageNotification.icon;
-            notificationManager.cancel(notificationId);
-        }
-        
-        if (visible) {
-            Resources r = Resources.getSystem();
-            CharSequence title = r.getText(titleId);
-            CharSequence message = r.getText(messageId);
-
-            if (mMediaStorageNotification == null) {
-                mMediaStorageNotification = new Notification();
-                mMediaStorageNotification.when = 0;
-            }
-
-            if (mPlaySounds) {
-                mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND;
-            } else {
-                mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
-            }
-
-            if (dismissable) {
-                mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
-            } else {
-                mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
-            }
-
-            mMediaStorageNotification.tickerText = title;
-            if (pi == null) {
-                Intent intent = new Intent();
-                pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
-            }
-
-            mMediaStorageNotification.icon = icon;
-            mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
-        }
-    
-        final int notificationId = mMediaStorageNotification.icon;
-        if (visible) {
-            notificationManager.notify(notificationId, mMediaStorageNotification);
-        } else {
-            notificationManager.cancel(notificationId);
+    public String[] getSecureContainerList() {
+        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        try {
+            return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
+        } catch (NativeDaemonConnectorException e) {
+            return new String[0];
         }
     }
 
-    public String[] getSecureContainerList() throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_ACCESS)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_ACCESS permission");
+    public int createSecureContainer(String id, int sizeMb, String fstype,
+                                    String key, int ownerUid) {
+        validatePermission(android.Manifest.permission.ASEC_CREATE);
+
+        int rc = MountServiceResultCode.OperationSucceeded;
+        String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);
+        try {
+            mConnector.doCommand(cmd);
+        } catch (NativeDaemonConnectorException e) {
+            rc = MountServiceResultCode.OperationFailedInternalError;
         }
-        return mConnector.doListCommand("list_asec", VoldResponseCode.AsecListResult);
+        return rc;
     }
 
-    public String createSecureContainer(String id, int sizeMb, String fstype,
-                                    String key, int ownerUid) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_CREATE)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_CREATE permission");
+    public int finalizeSecureContainer(String id) {
+        validatePermission(android.Manifest.permission.ASEC_CREATE);
+
+        int rc = MountServiceResultCode.OperationSucceeded;
+        try {
+            mConnector.doCommand(String.format("asec finalize %s", id));
+        } catch (NativeDaemonConnectorException e) {
+            rc = MountServiceResultCode.OperationFailedInternalError;
         }
-        String cmd = String.format("create_asec %s %d %s %s %d",
-                                   id, sizeMb, fstype, key, ownerUid);
-        mConnector.doCommand(cmd);
-        return getSecureContainerPath(id);
+        return rc;
     }
 
-    public void finalizeSecureContainer(String id) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_CREATE)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_CREATE permission");
-        }
-        mConnector.doCommand(String.format("finalize_asec %s", id));
-    }
+    public int destroySecureContainer(String id) {
+        validatePermission(android.Manifest.permission.ASEC_DESTROY);
 
-    public void destroySecureContainer(String id) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_DESTROY)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_DESTROY permission");
+        int rc = MountServiceResultCode.OperationSucceeded;
+        try {
+            mConnector.doCommand(String.format("asec destroy %s", id));
+        } catch (NativeDaemonConnectorException e) {
+            rc = MountServiceResultCode.OperationFailedInternalError;
         }
-        mConnector.doCommand(String.format("destroy_asec %s", id));
+        return rc;
     }
    
-    public String mountSecureContainer(String id, String key,
-                                       int ownerUid) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_MOUNT_UNMOUNT)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_MOUNT_UNMOUNT permission");
+    public int mountSecureContainer(String id, String key, int ownerUid) {
+        validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+
+        int rc = MountServiceResultCode.OperationSucceeded;
+        String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
+        try {
+            mConnector.doCommand(cmd);
+        } catch (NativeDaemonConnectorException e) {
+            rc = MountServiceResultCode.OperationFailedInternalError;
         }
-        String cmd = String.format("mount_asec %s %s %d",
-                                   id, key, ownerUid);
-        mConnector.doCommand(cmd);
-        return getSecureContainerPath(id);
+        return rc;
     }
 
-    public void unmountSecureContainer(String id) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_MOUNT_UNMOUNT)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_MOUNT_UNMOUNT permission");
+    public int unmountSecureContainer(String id) {
+        validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+
+        int rc = MountServiceResultCode.OperationSucceeded;
+        String cmd = String.format("asec unmount %s", id);
+        try {
+            mConnector.doCommand(cmd);
+        } catch (NativeDaemonConnectorException e) {
+            rc = MountServiceResultCode.OperationFailedInternalError;
         }
-        String cmd = String.format("unmount_asec %s", id);
-        mConnector.doCommand(cmd);
+        return rc;
     }
 
-    public void renameSecureContainer(String oldId, String newId) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_RENAME)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_RENAME permission");
+    public int renameSecureContainer(String oldId, String newId) {
+        validatePermission(android.Manifest.permission.ASEC_RENAME);
+
+        int rc = MountServiceResultCode.OperationSucceeded;
+        String cmd = String.format("asec rename %s %s", oldId, newId);
+        try {
+            mConnector.doCommand(cmd);
+        } catch (NativeDaemonConnectorException e) {
+            rc = MountServiceResultCode.OperationFailedInternalError;
         }
-        String cmd = String.format("rename_asec %s %s", oldId, newId);
-        mConnector.doCommand(cmd);
+        return rc;
     }
 
-    public String getSecureContainerPath(String id) throws IllegalStateException {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.ASEC_ACCESS)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ASEC_ACCESS permission");
-        }
-        ArrayList<String> rsp = mConnector.doCommand("asec_path " + id);
+    public String getSecureContainerPath(String id) {
+        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        ArrayList<String> rsp = mConnector.doCommand("asec path " + id);
 
         for (String line : rsp) {
             String []tok = line.split(" ");
@@ -1144,10 +855,13 @@
             if (code == VoldResponseCode.AsecPathResult) {
                 return tok[1];
             } else {
-                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+                Log.e(TAG, String.format("Unexpected response code %d", code));
+                return "";
             }
         }
-        throw new IllegalStateException("Got an empty response");
+
+        Log.e(TAG, "Got an empty response");
+        return "";
     }
 }