services: MountService: Refactor MountService for vold2
Squash of the following:
services: MountService: Rework the way volume states are handled
MountService: Add new API for directly getting volume state via a mount point
Environment: Switch from using system property for external storage state.
MountService: Add support for UMS
MountService: Fix a few bugs
services: MountService: Add support for mount-on-insertion
services: MountService: Add some debugging around UMS
services: MountService: Fix some UMS bugs and clean-up startup mount code
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 204389e..93617d1 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -45,6 +45,19 @@
private static final String TAG = "MountService";
+ class VolumeState {
+ public static final int Init = -1;
+ public static final int NoMedia = 0;
+ public static final int Idle = 1;
+ public static final int Pending = 2;
+ public static final int Checking = 3;
+ public static final int Mounted = 4;
+ public static final int Unmounting = 5;
+ public static final int Formatting = 6;
+ public static final int Shared = 7;
+ public static final int SharedMnt = 8;
+ }
+
/**
* Binder context for this service
*/
@@ -84,6 +97,11 @@
private boolean mAutoStartUms;
+ private boolean mUmsConnected = false;
+ private boolean mUmsEnabled = false;
+
+ private String mLegacyState = Environment.MEDIA_REMOVED;
+
/**
* Constructs a new MountService instance
*
@@ -119,7 +137,7 @@
* @return true if USB mass storage support is enabled.
*/
public boolean getMassStorageEnabled() throws RemoteException {
- return mListener.getMassStorageEnabled();
+ return mUmsEnabled;
}
/**
@@ -128,15 +146,53 @@
* @param enable true to enable USB mass storage support
*/
public void setMassStorageEnabled(boolean enable) throws RemoteException {
- mListener.setMassStorageEnabled(enable);
+ try {
+ String vp = Environment.getExternalStorageDirectory().getPath();
+ String vs = getVolumeState(vp);
+
+ if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
+ Log.d(TAG, "Unmounting media before UMS enable");
+ unmountMedia(vp);
+ }
+
+ mListener.setShareMethodEnabled(Environment
+ .getExternalStorageDirectory()
+ .getPath(),
+ "ums", enable);
+ mUmsEnabled = enable;
+ if (!enable) {
+ Log.d(TAG, "Mounting media after UMS disable");
+ mountMedia(vp);
+ }
+ } catch (RemoteException rex) {
+ Log.e(TAG, "Failed to set ums enable {" + enable + "}");
+ return;
+ }
}
/**
* @return true if USB mass storage is connected.
*/
public boolean getMassStorageConnected() throws RemoteException {
- return mListener.getMassStorageConnected();
+ return mUmsConnected;
}
+
+ /**
+ * @return state of the volume at the specified mount point
+ */
+ public String getVolumeState(String mountPoint) throws RemoteException {
+ /*
+ * 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;
+ }
+
/**
* Attempt to mount external media
@@ -147,7 +203,7 @@
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
}
- mListener.mountMedia(mountPath);
+ mListener.mountVolume(mountPath);
}
/**
@@ -165,7 +221,7 @@
mShowSafeUnmountNotificationWhenUnmounted = true;
// tell mountd to unmount the media
- mListener.ejectMedia(mountPath);
+ mListener.unmountVolume(mountPath);
}
/**
@@ -178,7 +234,7 @@
throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
}
- mListener.formatMedia(formatPath);
+ mListener.formatVolume(formatPath);
}
/**
@@ -221,6 +277,15 @@
SystemProperties.set("persist.service.mount.umsauto", (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.w(TAG, "State for {" + mountPoint + "} = {" + state + "}");
+ mLegacyState = state;
+ }
+
/**
* Update the state of the USB mass storage notification
*/
@@ -255,10 +320,73 @@
}
}
+ void onVoldConnected() {
+ new Thread() {
+ public void run() {
+ try {
+ if (!getVolumeState(Environment.getExternalStorageDirectory().getPath())
+ .equals(Environment.MEDIA_MOUNTED)) {
+ try {
+ mountMedia(Environment.getExternalStorageDirectory().getPath());
+ Log.d(TAG, "Connection-mount suceeded");
+ } catch (Exception ex) {
+ Log.w(TAG, "Connection-mount failed");
+ }
+ } else {
+ Log.d(TAG, "Skipping connection-mount; already mounted");
+ }
+ } catch (RemoteException rex) {
+ Log.e(TAG, "Exception while handling connection mount " + rex);
+ }
+
+ try {
+ boolean avail = mListener.getShareAvailable("ums");
+ notifyShareAvailabilityChange("ums", avail);
+ } catch (Exception ex) {
+ Log.w(TAG, "Failed to get share availability");
+ }
+ }
+ }.start();
+ }
+
+ void notifyVolumeStateChange(String label, String mountPoint, int oldState,
+ int newState) throws RemoteException {
+ 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, or UNMOUNTABLE
+ if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) &&
+ !vs.equals(Environment.MEDIA_NOFS) &&
+ !vs.equals(Environment.MEDIA_UNMOUNTABLE)) {
+ 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) &&
@@ -278,28 +406,64 @@
mContext.sendBroadcast(intent);
}
+ void notifyShareAvailabilityChange(String method, boolean avail) {
+ Log.d(TAG, "Share method {" + method + "} availability now " + avail);
+ if (!method.equals("ums")) {
+ Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
+ return;
+ }
+ if (avail) {
+ notifyUmsConnected();
+ } else {
+ notifyUmsDisconnected();
+ }
+ }
+
/**
* Broadcasts the USB mass storage disconnected event to all clients.
*/
void notifyUmsDisconnected() {
+ mUmsConnected = false;
updateUsbMassStorageNotification(false, false);
Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
mContext.sendBroadcast(intent);
}
+ void notifyMediaInserted(final String path) throws RemoteException {
+ new Thread() {
+ public void run() {
+ try {
+ Log.d(TAG, "Mounting media after insertion");
+ mountMedia(path);
+ } catch (Exception ex) {
+ Log.w(TAG, "Failed to mount media on insertion");
+ }
+ }
+ }.start();
+ }
+
/**
* Broadcasts the media removed event to all clients.
*/
- void notifyMediaRemoved(String path) {
+ void notifyMediaRemoved(String path) throws RemoteException {
+
+ // 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);
+ 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);
+ // Log.d(TAG, "Sending ACTION_MEDIA_REMOVED");
Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED,
Uri.parse("file://" + path));
mContext.sendBroadcast(intent);
@@ -309,6 +473,9 @@
* Broadcasts the media unmounted event to all clients.
*/
void notifyMediaUnmounted(String path) {
+
+ updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+
if (mShowSafeUnmountNotificationWhenUnmounted) {
setMediaStorageNotification(
com.android.internal.R.string.ext_media_safe_unmount_notification_title,
@@ -321,6 +488,7 @@
}
updateUsbMassStorageNotification(false, false);
+ // Log.d(TAG, "Sending ACTION_MEDIA_UNMOUNTED");
Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
Uri.parse("file://" + path));
mContext.sendBroadcast(intent);
@@ -330,6 +498,8 @@
* 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,
@@ -337,6 +507,7 @@
true, false, null);
updateUsbMassStorageNotification(true, false);
+ // Log.d(TAG, "Sending ACTION_MEDIA_CHECKING");
Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING,
Uri.parse("file://" + path));
mContext.sendBroadcast(intent);
@@ -346,6 +517,7 @@
* 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);
@@ -356,6 +528,7 @@
com.android.internal.R.drawable.stat_notify_sdcard_usb,
true, false, pi);
updateUsbMassStorageNotification(false, false);
+ // Log.d(TAG, "Sending ACTION_MEDIA_NOFS");
intent = new Intent(Intent.ACTION_MEDIA_NOFS,
Uri.parse("file://" + path));
mContext.sendBroadcast(intent);
@@ -365,8 +538,11 @@
* Broadcasts the media mounted event to all clients.
*/
void notifyMediaMounted(String path, boolean readOnly) {
+ updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
+
setMediaStorageNotification(0, 0, 0, false, false, null);
updateUsbMassStorageNotification(false, false);
+ // Log.d(TAG, "Sending ACTION_MEDIA_MOUNTED");
Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + path));
intent.putExtra("read-only", readOnly);
@@ -377,7 +553,14 @@
/**
* Broadcasts the media shared event to all clients.
*/
- void notifyMediaShared(String path) {
+ void notifyMediaShared(String path, boolean mounted) {
+ if (mounted) {
+ Log.e(TAG, "Live shared mounts not supported yet!");
+ return;
+ }
+
+ updatePublicVolumeState(path, Environment.MEDIA_SHARED);
+
Intent intent = new Intent();
intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
@@ -386,6 +569,7 @@
com.android.internal.R.drawable.stat_sys_warning,
false, true, pi);
handlePossibleExplicitUnmountBroadcast(path);
+ // Log.d(TAG, "Sending ACTION_MEDIA_SHARED");
intent = new Intent(Intent.ACTION_MEDIA_SHARED,
Uri.parse("file://" + path));
mContext.sendBroadcast(intent);
@@ -395,6 +579,8 @@
* 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,
@@ -402,19 +588,18 @@
true, true, null);
handlePossibleExplicitUnmountBroadcast(path);
+ // Log.d(TAG, "Sending ACTION_MEDIA_BAD_REMOVAL");
Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL,
Uri.parse("file://" + path));
mContext.sendBroadcast(intent);
-
- intent = new Intent(Intent.ACTION_MEDIA_REMOVED,
- 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);
@@ -427,6 +612,7 @@
handlePossibleExplicitUnmountBroadcast(path);
+ // Log.d(TAG, "Sending ACTION_MEDIA_UNMOUNTABLE");
intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE,
Uri.parse("file://" + path));
mContext.sendBroadcast(intent);
@@ -435,7 +621,8 @@
/**
* Broadcasts the media eject event to all clients.
*/
- void notifyMediaEject(String path) {
+ void notifyMediaUnmounting(String path) {
+ // Log.d(TAG, "Sending ACTION_MEDIA_EJECT");
Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT,
Uri.parse("file://" + path));
mContext.sendBroadcast(intent);