blob: 03480d19ba89a81095bea9714dbbde91510f83a2 [file] [log] [blame]
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.app.Notification;
20import android.app.NotificationManager;
21import android.app.PendingIntent;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.PackageManager;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080025import android.content.res.Resources;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070026import android.net.Uri;
27import android.os.IMountService;
28import android.os.Environment;
29import android.os.RemoteException;
30import android.os.UEventObserver;
31import android.util.Log;
32
33import java.io.File;
34import java.io.FileReader;
35
36/**
37 * MountService implements an to the mount service daemon
38 * @hide
39 */
40class MountService extends IMountService.Stub {
41
42 private static final String TAG = "MountService";
43
44 /**
45 * Binder context for this service
46 */
47 private Context mContext;
48
49 /**
50 * listener object for communicating with the mount service daemon
51 */
52 private MountListener mListener;
53
54 /**
55 * The notification that is shown when USB is connected. It leads the user
56 * to a dialog to enable mass storage mode.
57 * <p>
58 * This is lazily created, so use {@link #getUsbStorageNotification()}.
59 */
60 private Notification mUsbStorageNotification;
61
62 private class SdDoorListener extends UEventObserver {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080063 static final String SD_DOOR_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/sd-door";
64 static final String SD_DOOR_SWITCH_NAME = "sd-door";
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070065
66 public void onUEvent(UEvent event) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080067 if (SD_DOOR_SWITCH_NAME.equals(event.get("SWITCH_NAME"))) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070068 sdDoorStateChanged(event.get("SWITCH_STATE"));
69 }
70 }
71 };
72
73 /**
74 * Constructs a new MountService instance
75 *
76 * @param context Binder context for this service
77 */
78 public MountService(Context context) {
79 mContext = context;
80 mListener = new MountListener(this);
81 Thread thread = new Thread(mListener, MountListener.class.getName());
82 thread.start();
83 SdDoorListener sdDoorListener = new SdDoorListener();
84 sdDoorListener.startObserving(SdDoorListener.SD_DOOR_UEVENT_MATCH);
85 }
86
87 /**
88 * @return true if USB mass storage support is enabled.
89 */
90 public boolean getMassStorageEnabled() throws RemoteException {
91 return mListener.getMassStorageEnabled();
92 }
93
94 /**
95 * Enables or disables USB mass storage support.
96 *
97 * @param enable true to enable USB mass storage support
98 */
99 public void setMassStorageEnabled(boolean enable) throws RemoteException {
100 mListener.setMassStorageEnabled(enable);
101 }
102
103 /**
104 * @return true if USB mass storage is connected.
105 */
106 public boolean getMassStorageConnected() throws RemoteException {
107 return mListener.getMassStorageConnected();
108 }
109
110 /**
111 * Attempt to mount external media
112 */
113 public void mountMedia(String mountPath) throws RemoteException {
114 if (mContext.checkCallingOrSelfPermission(
115 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
116 != PackageManager.PERMISSION_GRANTED) {
117 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
118 }
119 mListener.mountMedia(mountPath);
120 }
121
122 /**
123 * Attempt to unmount external media to prepare for eject
124 */
125 public void unmountMedia(String mountPath) throws RemoteException {
126 if (mContext.checkCallingOrSelfPermission(
127 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
128 != PackageManager.PERMISSION_GRANTED) {
129 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
130 }
131
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800132 // tell mountd to unmount the media
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700133 mListener.ejectMedia(mountPath);
134 }
135
136 /**
137 * Broadcasts the USB mass storage connected event to all clients.
138 */
139 void notifyUmsConnected() {
140 setUsbStorageNotificationVisibility(true);
141 Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
142 mContext.sendBroadcast(intent);
143 }
144
145 /**
146 * Broadcasts the USB mass storage disconnected event to all clients.
147 */
148 void notifyUmsDisconnected() {
149 setUsbStorageNotificationVisibility(false);
150 Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
151 mContext.sendBroadcast(intent);
152 }
153
154 /**
155 * Broadcasts the media removed event to all clients.
156 */
157 void notifyMediaRemoved(String path) {
158 Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED,
159 Uri.parse("file://" + path));
160 mContext.sendBroadcast(intent);
161 }
162
163 /**
164 * Broadcasts the media unmounted event to all clients.
165 */
166 void notifyMediaUnmounted(String path) {
167 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
168 Uri.parse("file://" + path));
169 mContext.sendBroadcast(intent);
170 }
171
172 /**
173 * Broadcasts the media mounted event to all clients.
174 */
175 void notifyMediaMounted(String path, boolean readOnly) {
176 Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
177 Uri.parse("file://" + path));
178 intent.putExtra("read-only", readOnly);
179 mContext.sendBroadcast(intent);
180 }
181
182 /**
183 * Broadcasts the media shared event to all clients.
184 */
185 void notifyMediaShared(String path) {
186 Intent intent = new Intent(Intent.ACTION_MEDIA_SHARED,
187 Uri.parse("file://" + path));
188 mContext.sendBroadcast(intent);
189 }
190
191 /**
192 * Broadcasts the media bad removal event to all clients.
193 */
194 void notifyMediaBadRemoval(String path) {
195 Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL,
196 Uri.parse("file://" + path));
197 mContext.sendBroadcast(intent);
198 }
199
200 /**
201 * Broadcasts the media unmountable event to all clients.
202 */
203 void notifyMediaUnmountable(String path) {
204 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE,
205 Uri.parse("file://" + path));
206 mContext.sendBroadcast(intent);
207 }
208
209 /**
210 * Broadcasts the media eject event to all clients.
211 */
212 void notifyMediaEject(String path) {
213 Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT,
214 Uri.parse("file://" + path));
215 mContext.sendBroadcast(intent);
216 }
217
218 private void sdDoorStateChanged(String doorState) {
219 File directory = Environment.getExternalStorageDirectory();
220 String storageState = Environment.getExternalStorageState();
221
222 if (directory != null) {
223 try {
224 if (doorState.equals("open") && (storageState.equals(Environment.MEDIA_MOUNTED) ||
225 storageState.equals(Environment.MEDIA_MOUNTED_READ_ONLY))) {
226 // request SD card unmount if SD card door is opened
227 unmountMedia(directory.getPath());
228 } else if (doorState.equals("closed") && storageState.equals(Environment.MEDIA_UNMOUNTED)) {
229 // attempt to remount SD card
230 mountMedia(directory.getPath());
231 }
232 } catch (RemoteException e) {
233 // Nothing to do.
234 }
235 }
236 }
237
238 /**
239 * Sets the visibility of the USB storage notification. This should be
240 * called when a USB cable is connected and also when it is disconnected.
241 *
242 * @param visible Whether to show or hide the notification.
243 */
244 private void setUsbStorageNotificationVisibility(boolean visible) {
245 NotificationManager notificationManager = (NotificationManager) mContext
246 .getSystemService(Context.NOTIFICATION_SERVICE);
247 if (notificationManager == null) {
248 return;
249 }
250
251 /*
252 * The convention for notification IDs is to use the icon's resource ID
253 * when the icon is only used by a single notification type, which is
254 * the case here.
255 */
256 Notification notification = getUsbStorageNotification();
257 final int notificationId = notification.icon;
258
259 if (visible) {
260 notificationManager.notify(notificationId, notification);
261 } else {
262 notificationManager.cancel(notificationId);
263 }
264 }
265
266 /**
267 * Gets the USB storage notification.
268 *
269 * @return A {@link Notification} that leads to the dialog to enable USB storage.
270 */
271 private synchronized Notification getUsbStorageNotification() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800272 Resources r = Resources.getSystem();
273 CharSequence title =
274 r.getText(com.android.internal.R.string.usb_storage_notification_title);
275 CharSequence message =
276 r.getText(com.android.internal.R.string.usb_storage_notification_message);
277
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700278 if (mUsbStorageNotification == null) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700279 mUsbStorageNotification = new Notification();
280 mUsbStorageNotification.icon = com.android.internal.R.drawable.stat_sys_data_usb;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700281 mUsbStorageNotification.when = 0;
282 mUsbStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
283 mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700284 }
285
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800286 mUsbStorageNotification.tickerText = title;
287 mUsbStorageNotification.setLatestEventInfo(mContext, title, message,
288 getUsbStorageDialogIntent());
289
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700290 return mUsbStorageNotification;
291 }
292
293 /**
294 * Creates a pending intent to start the USB storage activity.
295 *
296 * @return A {@link PendingIntent} that start the USB storage activity.
297 */
298 private PendingIntent getUsbStorageDialogIntent() {
299 Intent intent = new Intent();
300 intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
301 return PendingIntent.getActivity(mContext, 0, intent, 0);
302 }
303}
304