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>
This commit is contained in:
San Mehat
2009-12-17 07:12:23 -08:00
parent 8af9649d44
commit 7fd0fee968
4 changed files with 407 additions and 216 deletions

View File

@@ -18,6 +18,8 @@ package android.os;
import java.io.File;
import android.os.IMountService;
/**
* Provides access to environment variables.
*/
@@ -28,6 +30,8 @@ public class Environment {
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 @@ public class Environment {
/**
* 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) {

View File

@@ -75,4 +75,9 @@ interface IMountService
* when a UMS host is detected.
*/
void setAutoStartUms(boolean value);
/**
* Gets the state of an volume via it's mountpoint.
*/
String getVolumeState(String mountPoint);
}

View File

@@ -21,6 +21,7 @@ import android.net.LocalSocket;
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.InputStream;
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 @@ final class MountListener implements Runnable {
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 @@ final class MountListener implements Runnable {
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 @@ final class MountListener implements Runnable {
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 @@ final class MountListener implements Runnable {
*
* @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);
}
}

View File

@@ -45,6 +45,19 @@ class MountService extends IMountService.Stub {
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 @@ class MountService extends IMountService.Stub {
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 @@ class MountService extends IMountService.Stub {
* @return true if USB mass storage support is enabled.
*/
public boolean getMassStorageEnabled() throws RemoteException {
return mListener.getMassStorageEnabled();
return mUmsEnabled;
}
/**
@@ -128,15 +146,53 @@ class MountService extends IMountService.Stub {
* @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 @@ class MountService extends IMountService.Stub {
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
}
mListener.mountMedia(mountPath);
mListener.mountVolume(mountPath);
}
/**
@@ -165,7 +221,7 @@ class MountService extends IMountService.Stub {
mShowSafeUnmountNotificationWhenUnmounted = true;
// tell mountd to unmount the media
mListener.ejectMedia(mountPath);
mListener.unmountVolume(mountPath);
}
/**
@@ -178,7 +234,7 @@ class MountService extends IMountService.Stub {
throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
}
mListener.formatMedia(formatPath);
mListener.formatVolume(formatPath);
}
/**
@@ -221,6 +277,15 @@ class MountService extends IMountService.Stub {
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 @@ class MountService extends IMountService.Stub {
}
}
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 @@ class MountService extends IMountService.Stub {
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 @@ class MountService extends IMountService.Stub {
* 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 @@ class MountService extends IMountService.Stub {
}
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 @@ class MountService extends IMountService.Stub {
* 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 @@ class MountService extends IMountService.Stub {
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 @@ class MountService extends IMountService.Stub {
* 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 @@ class MountService extends IMountService.Stub {
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 @@ class MountService extends IMountService.Stub {
* 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 @@ class MountService extends IMountService.Stub {
/**
* 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 @@ class MountService extends IMountService.Stub {
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 @@ class MountService extends IMountService.Stub {
* 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 @@ class MountService extends IMountService.Stub {
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 @@ class MountService extends IMountService.Stub {
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 @@ class MountService extends IMountService.Stub {
/**
* 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);