diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 6212b177cae66..eed2af7e6f9ee 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -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) { diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl index 96d44b6441dd8..447e764db6d24 100644 --- a/core/java/android/os/IMountService.aidl +++ b/core/java/android/os/IMountService.aidl @@ -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); } diff --git a/services/java/com/android/server/MountListener.java b/services/java/com/android/server/MountListener.java index 3e535851b9e91..d4943ffcc7dfd 100644 --- a/services/java/com/android/server/MountListener.java +++ b/services/java/com/android/server/MountListener.java @@ -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 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(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