diff options
Diffstat (limited to 'services/java/com/android/server/MountService.java')
-rw-r--r-- | services/java/com/android/server/MountService.java | 302 |
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); + } +} + |