summaryrefslogtreecommitdiff
path: root/services/java/com/android/server/MountService.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/MountService.java')
-rw-r--r--services/java/com/android/server/MountService.java302
1 files changed, 302 insertions, 0 deletions
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
new file mode 100644
index 000000000000..6f29332d5bc8
--- /dev/null
+++ b/services/java/com/android/server/MountService.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.IMountService;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.os.UEventObserver;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileReader;
+
+/**
+ * MountService implements an to the mount service daemon
+ * @hide
+ */
+class MountService extends IMountService.Stub {
+
+ private static final String TAG = "MountService";
+
+ /**
+ * Binder context for this service
+ */
+ private Context mContext;
+
+ /**
+ * listener object for communicating with the mount service daemon
+ */
+ private MountListener mListener;
+
+ /**
+ * The notification that is shown when USB is connected. It leads the user
+ * to a dialog to enable mass storage mode.
+ * <p>
+ * This is lazily created, so use {@link #getUsbStorageNotification()}.
+ */
+ private Notification mUsbStorageNotification;
+
+ private class SdDoorListener extends UEventObserver {
+ static final String SD_DOOR_UEVENT_MATCH = "DEVPATH=/class/switch/sd-door";
+
+ public void onUEvent(UEvent event) {
+ if ("sd-door".equals(event.get("SWITCH_NAME"))) {
+ sdDoorStateChanged(event.get("SWITCH_STATE"));
+ }
+ }
+ };
+
+ /**
+ * Constructs a new MountService instance
+ *
+ * @param context Binder context for this service
+ */
+ public MountService(Context context) {
+ mContext = context;
+ mListener = new MountListener(this);
+ Thread thread = new Thread(mListener, MountListener.class.getName());
+ thread.start();
+ SdDoorListener sdDoorListener = new SdDoorListener();
+ sdDoorListener.startObserving(SdDoorListener.SD_DOOR_UEVENT_MATCH);
+ }
+
+ /**
+ * @return true if USB mass storage support is enabled.
+ */
+ public boolean getMassStorageEnabled() throws RemoteException {
+ return mListener.getMassStorageEnabled();
+ }
+
+ /**
+ * Enables or disables USB mass storage support.
+ *
+ * @param enable true to enable USB mass storage support
+ */
+ public void setMassStorageEnabled(boolean enable) throws RemoteException {
+ mListener.setMassStorageEnabled(enable);
+ }
+
+ /**
+ * @return true if USB mass storage is connected.
+ */
+ public boolean getMassStorageConnected() throws RemoteException {
+ return mListener.getMassStorageConnected();
+ }
+
+ /**
+ * Attempt to mount external media
+ */
+ public void mountMedia(String mountPath) throws RemoteException {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
+ }
+ mListener.mountMedia(mountPath);
+ }
+
+ /**
+ * Attempt to unmount external media to prepare for eject
+ */
+ public void unmountMedia(String mountPath) throws RemoteException {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
+ }
+
+ // notify listeners that we are trying to eject
+ notifyMediaEject(mountPath);
+ // tell usbd to unmount the media
+ mListener.ejectMedia(mountPath);
+ }
+
+ /**
+ * Broadcasts the USB mass storage connected event to all clients.
+ */
+ void notifyUmsConnected() {
+ setUsbStorageNotificationVisibility(true);
+ Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Broadcasts the USB mass storage disconnected event to all clients.
+ */
+ void notifyUmsDisconnected() {
+ setUsbStorageNotificationVisibility(false);
+ Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Broadcasts the media removed event to all clients.
+ */
+ void notifyMediaRemoved(String 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) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
+ Uri.parse("file://" + path));
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Broadcasts the media mounted event to all clients.
+ */
+ void notifyMediaMounted(String path, boolean readOnly) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
+ Uri.parse("file://" + path));
+ intent.putExtra("read-only", readOnly);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Broadcasts the media shared event to all clients.
+ */
+ void notifyMediaShared(String 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) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL,
+ Uri.parse("file://" + path));
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Broadcasts the media unmountable event to all clients.
+ */
+ void notifyMediaUnmountable(String path) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE,
+ Uri.parse("file://" + path));
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Broadcasts the media eject event to all clients.
+ */
+ void notifyMediaEject(String path) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT,
+ Uri.parse("file://" + path));
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sdDoorStateChanged(String doorState) {
+ File directory = Environment.getExternalStorageDirectory();
+ String storageState = Environment.getExternalStorageState();
+
+ if (directory != null) {
+ try {
+ if (doorState.equals("open") && (storageState.equals(Environment.MEDIA_MOUNTED) ||
+ storageState.equals(Environment.MEDIA_MOUNTED_READ_ONLY))) {
+ // request SD card unmount if SD card door is opened
+ unmountMedia(directory.getPath());
+ } else if (doorState.equals("closed") && storageState.equals(Environment.MEDIA_UNMOUNTED)) {
+ // attempt to remount SD card
+ mountMedia(directory.getPath());
+ }
+ } catch (RemoteException e) {
+ // Nothing to do.
+ }
+ }
+ }
+
+ /**
+ * Sets the visibility of the USB storage notification. This should be
+ * called when a USB cable is connected and also when it is disconnected.
+ *
+ * @param visible Whether to show or hide the notification.
+ */
+ private void setUsbStorageNotificationVisibility(boolean visible) {
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ if (notificationManager == null) {
+ return;
+ }
+
+ /*
+ * The convention for notification IDs is to use the icon's resource ID
+ * when the icon is only used by a single notification type, which is
+ * the case here.
+ */
+ Notification notification = getUsbStorageNotification();
+ final int notificationId = notification.icon;
+
+ if (visible) {
+ notificationManager.notify(notificationId, notification);
+ } else {
+ notificationManager.cancel(notificationId);
+ }
+ }
+
+ /**
+ * Gets the USB storage notification.
+ *
+ * @return A {@link Notification} that leads to the dialog to enable USB storage.
+ */
+ private synchronized Notification getUsbStorageNotification() {
+ if (mUsbStorageNotification == null) {
+ CharSequence title =
+ mContext.getString(com.android.internal.R.string.usb_storage_notification_title);
+ CharSequence message =
+ mContext.getString(com.android.internal.R.string.usb_storage_notification_message);
+
+ mUsbStorageNotification = new Notification();
+ mUsbStorageNotification.icon = com.android.internal.R.drawable.stat_sys_data_usb;
+ mUsbStorageNotification.tickerText = title;
+ mUsbStorageNotification.when = 0;
+ mUsbStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
+ mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
+ mUsbStorageNotification.setLatestEventInfo(mContext, title, message,
+ getUsbStorageDialogIntent());
+ }
+
+ return mUsbStorageNotification;
+ }
+
+ /**
+ * Creates a pending intent to start the USB storage activity.
+ *
+ * @return A {@link PendingIntent} that start the USB storage activity.
+ */
+ private PendingIntent getUsbStorageDialogIntent() {
+ Intent intent = new Intent();
+ intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
+ return PendingIntent.getActivity(mContext, 0, intent, 0);
+ }
+}
+