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/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 6212b17..eed2af7 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -18,6 +18,8 @@
import java.io.File;
+import android.os.IMountService;
+
/**
* Provides access to environment variables.
*/
@@ -28,6 +30,8 @@
private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
+ private static IMountService mMntSvc = null;
+
/**
* Gets the Android root directory.
*/
@@ -167,9 +171,19 @@
/**
* Gets the current state of the external storage device.
+ * Note: This call should be deprecated as it doesn't support
+ * multiple volumes.
*/
public static String getExternalStorageState() {
- return SystemProperties.get("EXTERNAL_STORAGE_STATE", MEDIA_REMOVED);
+ try {
+ if (mMntSvc == null) {
+ mMntSvc = IMountService.Stub.asInterface(ServiceManager
+ .getService("mount"));
+ }
+ return mMntSvc.getVolumeState(getExternalStorageDirectory().toString());
+ } catch (android.os.RemoteException rex) {
+ return Environment.MEDIA_REMOVED;
+ }
}
static File getDirectory(String variableName, String defaultPath) {
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl
index 96d44b6..447e764 100644
--- a/core/java/android/os/IMountService.aidl
+++ b/core/java/android/os/IMountService.aidl
@@ -75,4 +75,9 @@
* when a UMS host is detected.
*/
void setAutoStartUms(boolean value);
+
+ /**
+ * Gets the state of an volume via it's mountpoint.
+ */
+ String getVolumeState(String mountPoint);
}
diff --git a/services/java/com/android/server/MountListener.java b/services/java/com/android/server/MountListener.java
index 3e53585..d4943ff 100644
--- a/services/java/com/android/server/MountListener.java
+++ b/services/java/com/android/server/MountListener.java
@@ -21,6 +21,7 @@
import android.os.Environment;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.RemoteException;
import android.util.Config;
import android.util.Log;
@@ -29,163 +30,59 @@
import java.io.OutputStream;
import java.net.Socket;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.ListIterator;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
/**
- * Thread for communicating with the vol service daemon via a local socket.
- * Events received from the daemon are passed to the MountService instance,
- * and the MountService instance calls MountListener to send commands to the daemon.
+ * Vold Connection class
*/
final class MountListener implements Runnable {
-
private static final String TAG = "MountListener";
-
- // ** THE FOLLOWING STRING CONSTANTS MUST MATCH VALUES IN system/vold/
-
- // socket name for connecting to vold
private static final String VOLD_SOCKET = "vold";
-
- // vold commands
- private static final String VOLD_CMD_ENABLE_UMS = "enable_ums";
- private static final String VOLD_CMD_DISABLE_UMS = "disable_ums";
- private static final String VOLD_CMD_SEND_UMS_STATUS = "send_ums_status";
- private static final String VOLD_CMD_MOUNT_VOLUME = "mount_volume:";
- private static final String VOLD_CMD_EJECT_MEDIA = "eject_media:";
- private static final String VOLD_CMD_FORMAT_MEDIA = "format_media:";
+ private static final int RESPONSE_QUEUE_SIZE = 10;
- // vold events
- private static final String VOLD_EVT_UMS_ENABLED = "ums_enabled";
- private static final String VOLD_EVT_UMS_DISABLED = "ums_disabled";
- private static final String VOLD_EVT_UMS_CONNECTED = "ums_connected";
- private static final String VOLD_EVT_UMS_DISCONNECTED = "ums_disconnected";
+ private MountService mService;
+ private BlockingQueue<String> mResponseQueue;
+ private OutputStream mOutputStream;
- private static final String VOLD_EVT_NOMEDIA = "volume_nomedia:";
- private static final String VOLD_EVT_UNMOUNTED = "volume_unmounted:";
- private static final String VOLD_EVT_MOUNTED = "volume_mounted:";
- private static final String VOLD_EVT_MOUNTED_RO = "volume_mounted_ro:";
- private static final String VOLD_EVT_UMS = "volume_ums";
- private static final String VOLD_EVT_BAD_REMOVAL = "volume_badremoval:";
- private static final String VOLD_EVT_DAMAGED = "volume_damaged:";
- private static final String VOLD_EVT_CHECKING = "volume_checking:";
- private static final String VOLD_EVT_NOFS = "volume_nofs:";
- private static final String VOLD_EVT_EJECTING = "volume_ejecting:";
+ class ResponseCode {
+ public static final int ShareAvailabilityResult = 210;
- /**
- * MountService that handles events received from the vol service daemon
- */
- private MountService mService;
-
- /**
- * Stream for sending commands to the vol service daemon.
- */
- private OutputStream mOutputStream;
-
- /**
- * Cached value indicating whether or not USB mass storage is enabled.
- */
- private boolean mUmsEnabled;
-
- /**
- * Cached value indicating whether or not USB mass storage is connected.
- */
- private boolean mUmsConnected;
-
- /**
- * Constructor for MountListener
- *
- * @param service The MountListener we are handling communication with USB
- * daemon for.
- */
- MountListener(MountService service) {
- mService = service;
+ public static final int UnsolicitedInformational = 600;
+ 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;
}
- /**
- * Process and dispatches events received from the vol service daemon
- *
- * @param event An event received from the vol service daemon
- */
- private void handleEvent(String event) {
- if (Config.LOGD) Log.d(TAG, "handleEvent " + event);
-
- int colonIndex = event.indexOf(':');
- String path = (colonIndex > 0 ? event.substring(colonIndex + 1) : null);
-
- if (event.equals(VOLD_EVT_UMS_ENABLED)) {
- mUmsEnabled = true;
- } else if (event.equals(VOLD_EVT_UMS_DISABLED)) {
- mUmsEnabled = false;
- } else if (event.equals(VOLD_EVT_UMS_CONNECTED)) {
- mUmsConnected = true;
- mService.notifyUmsConnected();
- } else if (event.equals(VOLD_EVT_UMS_DISCONNECTED)) {
- mUmsConnected = false;
- mService.notifyUmsDisconnected();
- } else if (event.startsWith(VOLD_EVT_NOMEDIA)) {
- mService.notifyMediaRemoved(path);
- } else if (event.startsWith(VOLD_EVT_UNMOUNTED)) {
- mService.notifyMediaUnmounted(path);
- } else if (event.startsWith(VOLD_EVT_CHECKING)) {
- mService.notifyMediaChecking(path);
- } else if (event.startsWith(VOLD_EVT_NOFS)) {
- mService.notifyMediaNoFs(path);
- } else if (event.startsWith(VOLD_EVT_MOUNTED)) {
- mService.notifyMediaMounted(path, false);
- } else if (event.startsWith(VOLD_EVT_MOUNTED_RO)) {
- mService.notifyMediaMounted(path, true);
- } else if (event.startsWith(VOLD_EVT_UMS)) {
- mService.notifyMediaShared(path);
- } else if (event.startsWith(VOLD_EVT_BAD_REMOVAL)) {
- mService.notifyMediaBadRemoval(path);
- // also send media eject intent, to notify apps to close any open
- // files on the media.
- mService.notifyMediaEject(path);
- } else if (event.startsWith(VOLD_EVT_DAMAGED)) {
- mService.notifyMediaUnmountable(path);
- } else if (event.startsWith(VOLD_EVT_EJECTING)) {
- mService.notifyMediaEject(path);
- }
+ MountListener(MountService service) {
+ mService = service;
+ mResponseQueue = new LinkedBlockingQueue<String>(RESPONSE_QUEUE_SIZE);
}
-
- /**
- * Sends a command to the mount service daemon via a local socket
- *
- * @param command The command to send to the mount service daemon
- */
- private void writeCommand(String command) {
- writeCommand2(command, null);
- }
-
- /**
- * Sends a command to the mount service daemon via a local socket
- * with a single argument
- *
- * @param command The command to send to the mount service daemon
- * @param argument The argument to send with the command (or null)
- */
- private void writeCommand2(String command, String argument) {
- synchronized (this) {
- if (mOutputStream == null) {
- Log.e(TAG, "No connection to vold", new IllegalStateException());
- } else {
- StringBuilder builder = new StringBuilder(command);
- if (argument != null) {
- builder.append(argument);
- }
- builder.append('\0');
- try {
- mOutputStream.write(builder.toString().getBytes());
- } catch (IOException ex) {
- Log.e(TAG, "IOException in writeCommand", ex);
- }
+ public void run() {
+ // Vold does not run in the simulator, so fake out a mounted event to trigger the Media Scanner
+ if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
+ mService.notifyMediaMounted(Environment.getExternalStorageDirectory().getPath(), false);
+ return;
+ }
+
+ try {
+ while (true) {
+ listenToSocket();
}
+ } catch (Throwable t) {
+ Log.e(TAG, "Fatal error " + t + " in MountListener thread!");
}
}
- /**
- * Opens a socket to communicate with the mount service daemon and listens
- * for events from the daemon.
- *
- */
private void listenToSocket() {
LocalSocket socket = null;
@@ -195,15 +92,13 @@
LocalSocketAddress.Namespace.RESERVED);
socket.connect(address);
+ mService.onVoldConnected();
InputStream inputStream = socket.getInputStream();
mOutputStream = socket.getOutputStream();
- byte[] buffer = new byte[100];
+ byte[] buffer = new byte[4096];
- writeCommand(VOLD_CMD_SEND_UMS_STATUS);
- mountMedia(Environment.getExternalStorageDirectory().getAbsolutePath());
-
while (true) {
int count = inputStream.read(buffer);
if (count < 0) break;
@@ -212,16 +107,36 @@
for (int i = 0; i < count; i++) {
if (buffer[i] == 0) {
String event = new String(buffer, start, i - start);
- handleEvent(event);
+// Log.d(TAG, "Got packet {" + event + "}");
+
+ String[] tokens = event.split(" ");
+ try {
+ int code = Integer.parseInt(tokens[0]);
+
+ if (code >= ResponseCode.UnsolicitedInformational) {
+ try {
+ handleUnsolicitedEvent(code, event, tokens);
+ } catch (Exception ex) {
+ Log.e(TAG, "Error handling unsolicited event '" + event + "'");
+ Log.e(TAG, ex.toString());
+ }
+ } else {
+ try {
+ mResponseQueue.put(event);
+ } catch (InterruptedException ex) {
+ Log.e(TAG, "InterruptedException");
+ }
+ }
+ } catch (NumberFormatException nfe) {
+ Log.w(TAG,
+ "Unknown msg from Vold '" + event + "'");
+ }
start = i + 1;
}
}
}
} catch (IOException ex) {
- // This exception is normal when running in desktop simulator
- // where there is no mount daemon to talk to
-
- // log("IOException in listenToSocket");
+ Log.e(TAG, "IOException in listenToSocket");
}
synchronized (this) {
@@ -244,46 +159,123 @@
Log.w(TAG, "IOException closing socket");
}
- /*
- * Sleep before trying again.
- * This should not happen except while debugging.
- * Without this sleep, the emulator will spin and
- * create tons of throwaway LocalSockets, making
- * system_server GC constantly.
- */
- Log.e(TAG, "Failed to connect to vold", new IllegalStateException());
- SystemClock.sleep(2000);
+ Log.e(TAG, "Failed to connect to Vold", new IllegalStateException());
+ SystemClock.sleep(5000);
+ }
+
+ private void handleUnsolicitedEvent(int code, String raw, String[] cooked) throws RemoteException {
+// Log.d(TAG, "unsolicited {" + raw + "}");
+ if (code == ResponseCode.VolumeStateChange) {
+ // FMT: NNN Volume <label> <mountpoint> state changed from <old_#> (<old_str>) to <new_#> (<new_str>)
+ mService.notifyVolumeStateChange(cooked[2], cooked[3],
+ Integer.parseInt(cooked[7]),
+ Integer.parseInt(cooked[10]));
+ } else if (code == ResponseCode.VolumeMountFailedBlank) {
+ // FMT: NNN Volume <label> <mountpoint> mount failed - no supported file-systems
+ mService.notifyMediaNoFs(cooked[3]);
+ // FMT: NNN Volume <label> <mountpoint> mount failed - no media
+ } else if (code == ResponseCode.VolumeMountFailedNoMedia) {
+ mService.notifyMediaRemoved(cooked[3]);
+ } else if (code == ResponseCode.VolumeMountFailedDamaged) {
+ // FMT: NNN Volume <label> <mountpoint> mount failed - filesystem check failed
+ mService.notifyMediaUnmountable(cooked[3]);
+ } else if (code == ResponseCode.ShareAvailabilityChange) {
+ // FMT: NNN Share method <method> now <available|unavailable>
+ boolean avail = false;
+ if (cooked[5].equals("available")) {
+ avail = true;
+ }
+ mService.notifyShareAvailabilityChange(cooked[3], avail);
+ } else if (code == ResponseCode.VolumeDiskInserted) {
+ // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
+ mService.notifyMediaInserted(cooked[3]);
+ } else if (code == ResponseCode.VolumeDiskRemoved) {
+ // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
+ mService.notifyMediaRemoved(cooked[3]);
+ } else if (code == ResponseCode.VolumeBadRemoval) {
+ // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
+ mService.notifyMediaBadRemoval(cooked[3]);
+ } else {
+ Log.d(TAG, "Unhandled event {" + raw + "}");
+ }
+ }
+
+
+ private void sendCommand(String command) {
+ sendCommand(command, null);
}
/**
- * Main loop for MountListener thread.
+ * Sends a command to Vold with a single argument
+ *
+ * @param command The command to send to the mount service daemon
+ * @param argument The argument to send with the command (or null)
*/
- public void run() {
- // ugly hack for the simulator.
- if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
- SystemProperties.set("EXTERNAL_STORAGE_STATE", Environment.MEDIA_MOUNTED);
- // usbd does not run in the simulator, so send a fake device mounted event to trigger the Media Scanner
- mService.notifyMediaMounted(Environment.getExternalStorageDirectory().getPath(), false);
-
- // no usbd in the simulator, so no point in hanging around.
- return;
- }
-
- try {
- while (true) {
- listenToSocket();
+ private void sendCommand(String command, String argument) {
+ synchronized (this) {
+ Log.d(TAG, "sendCommand {" + command + "} {" + argument + "}");
+ if (mOutputStream == null) {
+ Log.e(TAG, "No connection to Vold", new IllegalStateException());
+ } else {
+ StringBuilder builder = new StringBuilder(command);
+ if (argument != null) {
+ builder.append(argument);
+ }
+ builder.append('\0');
+
+ try {
+ mOutputStream.write(builder.toString().getBytes());
+ } catch (IOException ex) {
+ Log.e(TAG, "IOException in sendCommand", ex);
+ }
}
- } catch (Throwable t) {
- // catch all Throwables so we don't bring down the system process
- Log.e(TAG, "Fatal error " + t + " in MountListener thread!");
}
}
-
- /**
- * @return true if USB mass storage is enabled
- */
- boolean getMassStorageEnabled() {
- return mUmsEnabled;
+
+ private synchronized ArrayList<String> doCommand(String cmd) throws RemoteException {
+ sendCommand(cmd);
+
+ ArrayList<String> response = new ArrayList<String>();
+ boolean complete = false;
+ int code = -1;
+
+ while (!complete) {
+ try {
+ String line = mResponseQueue.take();
+// Log.d(TAG, "Removed off queue -> " + line);
+ String[] tokens = line.split(" ");
+ code = Integer.parseInt(tokens[0]);
+
+ if ((code >= 200) && (code < 600))
+ complete = true;
+ response.add(line);
+ } catch (InterruptedException ex) {
+ Log.e(TAG, "InterruptedException");
+ }
+ }
+
+ if (code >= 400 && code < 600) {
+ Log.w(TAG, "Vold cmd {" + cmd + "} err code " + code);
+ throw new RemoteException();
+ }
+ return response;
+ }
+
+ boolean getShareAvailable(String method) throws RemoteException {
+ ArrayList<String> rsp = doCommand("share_available " + method);
+
+ for (String line : rsp) {
+ String []tok = line.split(" ");
+ int code = Integer.parseInt(tok[0]);
+ if (code == ResponseCode.ShareAvailabilityResult) {
+ if (tok[2].equals("available"))
+ return true;
+ return false;
+ } else {
+ throw new RemoteException();
+ }
+ }
+ throw new RemoteException();
}
/**
@@ -291,35 +283,28 @@
*
* @param enable true to enable USB mass storage support
*/
- void setMassStorageEnabled(boolean enable) {
- writeCommand(enable ? VOLD_CMD_ENABLE_UMS : VOLD_CMD_DISABLE_UMS);
- }
-
- /**
- * @return true if USB mass storage is connected
- */
- boolean getMassStorageConnected() {
- return mUmsConnected;
+ void setShareMethodEnabled(String mountPoint, String method, boolean enable) throws RemoteException {
+ doCommand((enable ? "" : "un") + "share " + mountPoint + " " + method);
}
/**
* Mount media at given mount point.
*/
- public void mountMedia(String mountPoint) {
- writeCommand2(VOLD_CMD_MOUNT_VOLUME, mountPoint);
+ public void mountVolume(String label) throws RemoteException {
+ doCommand("mount " + label);
}
/**
* Unmount media at given mount point.
*/
- public void ejectMedia(String mountPoint) {
- writeCommand2(VOLD_CMD_EJECT_MEDIA, mountPoint);
+ public void unmountVolume(String label) throws RemoteException {
+ doCommand("unmount " + label);
}
/**
* Format media at given mount point.
*/
- public void formatMedia(String mountPoint) {
- writeCommand2(VOLD_CMD_FORMAT_MEDIA, mountPoint);
+ public void formatVolume(String label) throws RemoteException {
+ doCommand("format " + label);
}
}
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);