diff options
author | San Mehat <san@google.com> | 2010-01-29 05:32:19 -0800 |
---|---|---|
committer | San Mehat <san@google.com> | 2010-02-02 11:17:46 -0800 |
commit | 4270e1ea74c57f1c65620e9f5ecaa8c2a5daf0e1 (patch) | |
tree | 151ed15644268731334b27a1e179ed556c1cd407 /services | |
parent | 78071b73479b9bea536dd1d5260ea78dd176640b (diff) | |
download | frameworks_base-4270e1ea74c57f1c65620e9f5ecaa8c2a5daf0e1.zip frameworks_base-4270e1ea74c57f1c65620e9f5ecaa8c2a5daf0e1.tar.gz frameworks_base-4270e1ea74c57f1c65620e9f5ecaa8c2a5daf0e1.tar.bz2 |
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>
Diffstat (limited to 'services')
-rw-r--r-- | services/java/com/android/server/MountService.java | 1312 |
1 files changed, 513 insertions, 799 deletions
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.pm.PackageManager; 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.text.TextUtils; 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 @@ class MountService extends IMountService.Stub 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 @@ class MountService extends IMountService.Stub 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 @@ class MountService extends IMountService.Stub 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 @@ class MountService extends IMountService.Stub 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 @@ class MountService extends IMountService.Stub * event to trigger MediaScanner */ if ("simulator".equals(SystemProperties.get("ro.product.device"))) { - notifyMediaMounted( - Environment.getExternalStorageDirectory().getPath(), false); + updatePublicVolumeState("/sdcard", Environment.MEDIA_MOUNTED); return; } @@ -244,280 +152,89 @@ class MountService extends IMountService.Stub } }; - public void shutdown() { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.SHUTDOWN) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires SHUTDOWN permission"); - } - - Log.d(TAG, "Shutting down"); - String state = Environment.getExternalStorageState(); + private final class MountServiceBinderListener implements IBinder.DeathRecipient { + final IMountServiceListener mListener; - if (state.equals(Environment.MEDIA_SHARED)) { - /* - * If the media is currently shared, unshare it. - * XXX: This is still dangerous!. We should not - * be rebooting at *all* if UMS is enabled, since - * 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); - } - } else if (state.equals(Environment.MEDIA_CHECKING)) { - /* - * If the media is being checked, then we need to wait for - * it to complete before being able to proceed. - */ - // XXX: @hackbod - Should we disable the ANR timer here? - int retries = 30; - while (state.equals(Environment.MEDIA_CHECKING) && (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 check"); - } + MountServiceBinderListener(IMountServiceListener listener) { + mListener = listener; + } - if (state.equals(Environment.MEDIA_MOUNTED)) { - /* - * If the media is mounted, then gracefully unmount it. - */ - 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); + public void binderDied() { + Log.d(TAG, "An IMountServiceListener has died!"); + synchronized(mListeners) { + mListeners.remove(this); + mListener.asBinder().unlinkToDeath(this, 0); } } } - /** - * @return true if USB mass storage support is enabled. - */ - public boolean getMassStorageEnabled() { - return mUmsEnabled; - } + int doShareUnshareVolume(String path, String method, boolean enable) { + validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - /** - * 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); - - mUmsEnabling = enable; - if (enable && vs.equals(Environment.MEDIA_MOUNTED)) { - unmountVolume(vp); - mUmsEnabling = false; - updateUsbMassStorageNotification(true, false); - } - - setShareMethodEnabled(vp, "ums", enable); - mUmsEnabled = enable; - mUmsEnabling = false; - if (!enable) { - mountVolume(vp); - if (mPromptUms) { - updateUsbMassStorageNotification(false, false); - } else { - updateUsbMassStorageNotification(true, false); - } - } - } catch (IllegalStateException rex) { - Log.e(TAG, "Failed to set ums enable {" + enable + "}"); - return; + // TODO: Add support for multiple share methods + if (!method.equals("ums")) { + throw new IllegalArgumentException(String.format("Method %s not supported", method)); } - } - /** - * @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 { /* - * XXX: Until we have multiple volume discovery, just hardwire - * this to /sdcard + * If the volume is mounted and we're enabling then unmount it */ - if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) { - Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume"); - throw new IllegalArgumentException(); - } - - return mLegacyState; - } - - - /** - * 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"); - } - mConnector.doCommand(String.format("mount %s", mountPath)); - } - - /** - * 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"); + String vs = getVolumeState(path); + if (enable && vs.equals(Environment.MEDIA_MOUNTED)) { + mUmsEnabling = enable; // Supress unmounted events + unmountVolume(path); + mUmsEnabling = false; // Unsupress unmounted events } - // 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"); + 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; } - 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)); + /* + * 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. + */ } } - 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")); + return MountServiceResultCode.OperationSucceeded; } - void updatePublicVolumeState(String mountPoint, String state) { - if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) { + 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 {" + mountPoint + "} = {" + state + "}"); - mLegacyState = state; - } + Log.i(TAG, "State for {" + path + "} = {" + state + "}"); - /** - * Update the state of the USB mass storage notification - */ - void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) { - - try { + String oldState = mLegacyState; + mLegacyState = state; - 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); - } else { - setUsbStorageNotification(0, 0, 0, false, false, null); + 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); + } } - } 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); } } @@ -540,7 +257,7 @@ class MountService extends IMountService.Stub try { String[] vols = mConnector.doListCommand( - "list_volumes", VoldResponseCode.VolumeListResult); + "volume list", VoldResponseCode.VolumeListResult); for (String volstr : vols) { String[] tok = volstr.split(" "); // FMT: <label> <mountpoint> <state> @@ -554,10 +271,9 @@ class MountService extends IMountService.Stub state = Environment.MEDIA_REMOVED; } else if (st == VolumeState.Idle) { state = null; - try { - mountVolume(path); - } catch (Exception ex) { - Log.e(TAG, "Connection-mount failed", ex); + 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; @@ -578,7 +294,7 @@ class MountService extends IMountService.Stub } try { - boolean avail = getShareAvailable("ums"); + boolean avail = getShareMethodAvailable("ums"); notifyShareAvailabilityChange("ums", avail); } catch (Exception ex) { Log.w(TAG, "Failed to get share availability"); @@ -592,22 +308,18 @@ class MountService extends IMountService.Stub * Callback from NativeDaemonConnector */ public boolean onEvent(int code, String raw, String[] cooked) { + Intent in = null; + // Log.d(TAG, "event {" + raw + "}"); if (code == VoldResponseCode.VolumeStateChange) { - // FMT: NNN Volume <label> <mountpoint> state changed - // from <old_#> (<old_str>) to <new_#> (<new_str>) + /* + * 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.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; @@ -615,24 +327,100 @@ class MountService extends IMountService.Stub avail = true; } notifyShareAvailabilityChange(cooked[3], avail); - } else if (code == VoldResponseCode.VolumeDiskInserted) { + } else if ((code == VoldResponseCode.VolumeDiskInserted) || + (code == VoldResponseCode.VolumeDiskRemoved) || + (code == VoldResponseCode.VolumeBadRemoval)) { // 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]); + 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 mountPoint, int oldState, - int newState) throws IllegalStateException { - String vs = getVolumeState(mountPoint); + 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) { @@ -642,53 +430,45 @@ class MountService extends IMountService.Stub * 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); + 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) { - notifyMediaChecking(mountPoint); + updatePublicVolumeState(path, Environment.MEDIA_CHECKING); + in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path)); } else if (newState == VolumeState.Mounted) { - notifyMediaMounted(mountPoint, false); + 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) { - notifyMediaUnmounting(mountPoint); + mPms.updateExternalMediaStatus(false); + in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path)); } else if (newState == VolumeState.Formatting) { } else if (newState == VolumeState.Shared) { - notifyMediaShared(mountPoint, false); + /* 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) { - notifyMediaShared(mountPoint, true); + Log.e(TAG, "Live shared mounts not supported yet!"); + return; } 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); - } + if (in != null) { + mContext.sendBroadcast(in); } - - Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED); - mContext.sendBroadcast(intent); } void notifyShareAvailabilityChange(String method, final boolean avail) { @@ -697,446 +477,377 @@ class MountService extends IMountService.Stub return; } - /* - * Notification needs to run in a different thread as - * it may need to call back into vold - */ - new Thread() { - public void run() { + synchronized (mListeners) { + for (int i = mListeners.size() -1; i >= 0; i--) { + MountServiceBinderListener bl = mListeners.get(i); try { - if (avail) { - notifyUmsConnected(); - } else { - notifyUmsDisconnected(); - } + bl.mListener.onShareAvailabilityChanged(method, avail); + } catch (RemoteException rex) { + Log.e(TAG, "Listener dead"); + mListeners.remove(i); } catch (Exception ex) { - Log.w(TAG, "Failed to mount media on insertion"); + Log.e(TAG, "Listener failed", ex); } } - }.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); - } + Intent intent; + if (avail) { + intent = new Intent(Intent.ACTION_UMS_CONNECTED); + } else { + intent = new Intent(Intent.ACTION_UMS_DISCONNECTED); } - 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(); + void validatePermission(String perm) { + if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException(String.format("Requires %s permission", perm)); + } } /** - * Broadcasts the media removed event to all clients. + * Exposed API calls below here */ - void notifyMediaRemoved(String path) throws IllegalStateException { - // Suppress this on bad removal - if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) { - return; + 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"); + } } - - 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); + public void unregisterListener(IMountServiceListener listener) { + synchronized (mListeners) { + for(MountServiceBinderListener bl : mListeners) { + if (bl.mListener == listener) { + mListeners.remove(mListeners.indexOf(bl)); + return; + } + } } - 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); - } + public void shutdown() { + validatePermission(android.Manifest.permission.SHUTDOWN); - /** - * 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); - } + Log.i(TAG, "Shutting down"); - /** - * 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); - } + String path = Environment.getExternalStorageDirectory().getPath(); + String state = getVolumeState(path); - /** - * 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; + if (state.equals(Environment.MEDIA_SHARED)) { + /* + * If the media is currently shared, unshare it. + * XXX: This is still dangerous!. We should not + * be rebooting at *all* if UMS is enabled, since + * the UMS host could have dirty FAT cache entries + * yet to flush. + */ + if (unshareVolume(path, "ums") != MountServiceResultCode.OperationSucceeded) { + Log.e(TAG, "UMS disable on shutdown failed"); + } + } else if (state.equals(Environment.MEDIA_CHECKING)) { + /* + * If the media is being checked, then we need to wait for + * it to complete before being able to proceed. + */ + // XXX: @hackbod - Should we disable the ANR timer here? + int retries = 30; + while (state.equals(Environment.MEDIA_CHECKING) && (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 check"); + } } - 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); + if (state.equals(Environment.MEDIA_MOUNTED)) { + /* + * If the media is mounted, then gracefully unmount it. + */ + if (unmountVolume(path) != MountServiceResultCode.OperationSucceeded) { + Log.e(TAG, "Failed to unmount media for shutdown"); + } } - 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); + public String[] getShareMethodList() { + String[] rdata = new String[1]; + rdata[0] = "ums"; + return rdata; } - /** - * Broadcasts the media unmountable event to all clients. - */ - void notifyMediaUnmountable(String path) { - updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE); + public boolean getShareMethodAvailable(String method) { + ArrayList<String> rsp = mConnector.doCommand("share status " + method); + + 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; + } + 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; + } - Intent intent = new Intent(); - intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + public int shareVolume(String path, String method) { + return doShareUnshareVolume(path, method, true); + } - 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); + public int unshareVolume(String path, String method) { + return doShareUnshareVolume(path, method, false); + } - handlePossibleExplicitUnmountBroadcast(path); + public boolean getVolumeShared(String path, String method) { + String cmd = String.format("volume shared %s %s", path, method); + ArrayList<String> rsp = mConnector.doCommand(cmd); - intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, - Uri.parse("file://" + path)); - mContext.sendBroadcast(intent); + 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; + } + 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; + } + } + Log.e(TAG, "Got an empty response"); + return false; } /** - * Broadcasts the media eject event to all clients. + * @return state of the volume at the specified mount point */ - void notifyMediaUnmounting(String path) { - Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT, - Uri.parse("file://" + path)); - mContext.sendBroadcast(intent); + public String getVolumeState(String mountPoint) { + /* + * XXX: Until we have multiple volume discovery, just hardwire + * this to /sdcard + */ + if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) { + Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume"); + throw new IllegalArgumentException(); + } + + return mLegacyState; } + /** - * Sets the USB storage notification. + * Attempt to mount external media */ - private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible, - PendingIntent pi) { + public int mountVolume(String path) { + validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - 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; - } + int rc = MountServiceResultCode.OperationSucceeded; - if (sound && mPlaySounds) { - mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; + 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 { - mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; + rc = 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); + /* + * Send broadcast intent (if required for the failure) + */ + if (in != null) { + mContext.sendBroadcast(in); } - - 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; + return rc; } /** - * Sets the media storage notification. + * Attempt to unmount external media to prepare for eject */ - private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible, - boolean dismissable, PendingIntent pi) { + public int unmountVolume(String path) { + validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - 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; + try { + 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 { - mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; + return MountServiceResultCode.OperationFailedInternalError; } + } + } - if (dismissable) { - mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; - } else { - mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; - } + /** + * Synchronously formats a volume + * + * @param path The volume path to format + * @return Error code from MountServiceResultCode + */ + public int formatVolume(String path) { + validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); - mMediaStorageNotification.tickerText = title; - if (pi == null) { - Intent intent = new Intent(); - pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); + 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 { + return MountServiceResultCode.OperationFailedInternalError; } - - 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() throws IllegalStateException { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.ASEC_ACCESS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires ASEC_ACCESS permission"); + public String[] getSecureContainerList() { + validatePermission(android.Manifest.permission.ASEC_ACCESS); + try { + return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult); + } catch (NativeDaemonConnectorException e) { + return new String[0]; } - return mConnector.doListCommand("list_asec", VoldResponseCode.AsecListResult); } - 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 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; } - 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"); + 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; } - mConnector.doCommand(String.format("finalize_asec %s", id)); + return rc; } - 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"); + public int destroySecureContainer(String id) { + validatePermission(android.Manifest.permission.ASEC_DESTROY); + + 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 @@ class MountService extends IMountService.Stub 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 ""; } } |