blob: 0feb1da7608415562464a8c82b55b27a98c6bf77 [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;
The Android Open Source Projectd24b8182009-02-10 15:44:00 -080022import android.content.BroadcastReceiver;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070023import android.content.Context;
24import android.content.Intent;
The Android Open Source Projectd24b8182009-02-10 15:44:00 -080025import android.content.IntentFilter;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070026import android.content.pm.PackageManager;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080027import android.content.res.Resources;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070028import android.net.Uri;
29import android.os.IMountService;
30import android.os.Environment;
31import android.os.RemoteException;
The Android Open Source Projectd24b8182009-02-10 15:44:00 -080032import android.os.SystemProperties;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070033import android.os.UEventObserver;
The Android Open Source Projectd24b8182009-02-10 15:44:00 -080034import android.text.TextUtils;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070035import android.util.Log;
36
37import java.io.File;
38import java.io.FileReader;
39
40/**
41 * MountService implements an to the mount service daemon
42 * @hide
43 */
44class MountService extends IMountService.Stub {
45
46 private static final String TAG = "MountService";
47
48 /**
49 * Binder context for this service
50 */
51 private Context mContext;
52
53 /**
54 * listener object for communicating with the mount service daemon
55 */
56 private MountListener mListener;
57
58 /**
The Android Open Source Projectd24b8182009-02-10 15:44:00 -080059 * The notification that is shown when a USB mass storage host
60 * is connected.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070061 * <p>
The Android Open Source Projectd24b8182009-02-10 15:44:00 -080062 * This is lazily created, so use {@link #setUsbStorageNotification()}.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070063 */
64 private Notification mUsbStorageNotification;
65
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070066
The Android Open Source Projectd24b8182009-02-10 15:44:00 -080067 /**
68 * The notification that is shown when the following media events occur:
69 * - Media is being checked
70 * - Media is blank (or unknown filesystem)
71 * - Media is corrupt
72 * - Media is safe to unmount
73 * - Media is missing
74 * <p>
75 * This is lazily created, so use {@link #setMediaStorageNotification()}.
76 */
77 private Notification mMediaStorageNotification;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070078
The Android Open Source Projectd24b8182009-02-10 15:44:00 -080079 private boolean mShowSafeUnmountNotificationWhenUnmounted;
80
81 private boolean mPlaySounds;
82
83 private boolean mMounted;
84
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070085 /**
86 * Constructs a new MountService instance
87 *
88 * @param context Binder context for this service
89 */
90 public MountService(Context context) {
91 mContext = context;
The Android Open Source Projectd24b8182009-02-10 15:44:00 -080092
93 // Register a BOOT_COMPLETED handler so that we can start
94 // MountListener. We defer the startup so that we don't
95 // start processing events before we ought-to
96 mContext.registerReceiver(mBroadcastReceiver,
97 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
98
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070099 mListener = new MountListener(this);
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800100 mShowSafeUnmountNotificationWhenUnmounted = false;
101
102 mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700103 }
104
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800105 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
106 public void onReceive(Context context, Intent intent) {
107 if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
108 Thread thread = new Thread(mListener, MountListener.class.getName());
109 thread.start();
110 }
111 }
112 };
113
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700114 /**
115 * @return true if USB mass storage support is enabled.
116 */
117 public boolean getMassStorageEnabled() throws RemoteException {
118 return mListener.getMassStorageEnabled();
119 }
120
121 /**
122 * Enables or disables USB mass storage support.
123 *
124 * @param enable true to enable USB mass storage support
125 */
126 public void setMassStorageEnabled(boolean enable) throws RemoteException {
127 mListener.setMassStorageEnabled(enable);
128 }
129
130 /**
131 * @return true if USB mass storage is connected.
132 */
133 public boolean getMassStorageConnected() throws RemoteException {
134 return mListener.getMassStorageConnected();
135 }
136
137 /**
138 * Attempt to mount external media
139 */
140 public void mountMedia(String mountPath) throws RemoteException {
141 if (mContext.checkCallingOrSelfPermission(
142 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
143 != PackageManager.PERMISSION_GRANTED) {
144 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
145 }
146 mListener.mountMedia(mountPath);
147 }
148
149 /**
150 * Attempt to unmount external media to prepare for eject
151 */
152 public void unmountMedia(String mountPath) throws RemoteException {
153 if (mContext.checkCallingOrSelfPermission(
154 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
155 != PackageManager.PERMISSION_GRANTED) {
156 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
157 }
158
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800159 // Set a flag so that when we get the unmounted event, we know
160 // to display the notification
161 mShowSafeUnmountNotificationWhenUnmounted = true;
162
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800163 // tell mountd to unmount the media
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700164 mListener.ejectMedia(mountPath);
165 }
166
167 /**
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800168 * Attempt to format external media
169 */
170 public void formatMedia(String formatPath) throws RemoteException {
171 if (mContext.checkCallingOrSelfPermission(
172 android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS)
173 != PackageManager.PERMISSION_GRANTED) {
174 throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
175 }
176
177 mListener.formatMedia(formatPath);
178 }
179
180 /**
181 * Returns true if we're playing media notification sounds.
182 */
183 public boolean getPlayNotificationSounds() {
184 return mPlaySounds;
185 }
186
187 /**
188 * Set whether or not we're playing media notification sounds.
189 */
190 public void setPlayNotificationSounds(boolean enabled) {
191 if (mContext.checkCallingOrSelfPermission(
192 android.Manifest.permission.WRITE_SETTINGS)
193 != PackageManager.PERMISSION_GRANTED) {
194 throw new SecurityException("Requires WRITE_SETTINGS permission");
195 }
196 mPlaySounds = enabled;
197 SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0"));
198 }
199
200 /**
201 * Update the state of the USB mass storage notification
202 */
203 void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) {
204
205 try {
206
207 if (getMassStorageConnected() && !suppressIfConnected) {
208 Intent intent = new Intent();
209 intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
210 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
211 setUsbStorageNotification(
212 com.android.internal.R.string.usb_storage_notification_title,
213 com.android.internal.R.string.usb_storage_notification_message,
214 com.android.internal.R.drawable.stat_sys_data_usb,
215 sound, true, pi);
216 } else {
217 setUsbStorageNotification(0, 0, 0, false, false, null);
218 }
219 } catch (RemoteException e) {
220 // Nothing to do
221 }
222 }
223
224 void handlePossibleExplicitUnmountBroadcast(String path) {
225 if (mMounted) {
226 mMounted = false;
227 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
228 Uri.parse("file://" + path));
229 mContext.sendBroadcast(intent);
230 }
231 }
232
233 /**
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700234 * Broadcasts the USB mass storage connected event to all clients.
235 */
236 void notifyUmsConnected() {
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800237 String storageState = Environment.getExternalStorageState();
238 if (!storageState.equals(Environment.MEDIA_REMOVED) &&
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800239 !storageState.equals(Environment.MEDIA_BAD_REMOVAL) &&
240 !storageState.equals(Environment.MEDIA_CHECKING)) {
241
242 updateUsbMassStorageNotification(false, true);
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800243 }
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800244
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700245 Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
246 mContext.sendBroadcast(intent);
247 }
248
249 /**
250 * Broadcasts the USB mass storage disconnected event to all clients.
251 */
252 void notifyUmsDisconnected() {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800253 updateUsbMassStorageNotification(false, false);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700254 Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
255 mContext.sendBroadcast(intent);
256 }
257
258 /**
259 * Broadcasts the media removed event to all clients.
260 */
261 void notifyMediaRemoved(String path) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800262 updateUsbMassStorageNotification(true, false);
263
264 setMediaStorageNotification(
265 com.android.internal.R.string.ext_media_nomedia_notification_title,
266 com.android.internal.R.string.ext_media_nomedia_notification_message,
267 com.android.internal.R.drawable.stat_sys_no_sim,
268 true, false, null);
269 handlePossibleExplicitUnmountBroadcast(path);
270
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700271 Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED,
272 Uri.parse("file://" + path));
273 mContext.sendBroadcast(intent);
274 }
275
276 /**
277 * Broadcasts the media unmounted event to all clients.
278 */
279 void notifyMediaUnmounted(String path) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800280 if (mShowSafeUnmountNotificationWhenUnmounted) {
281 setMediaStorageNotification(
282 com.android.internal.R.string.ext_media_safe_unmount_notification_title,
283 com.android.internal.R.string.ext_media_safe_unmount_notification_message,
284 com.android.internal.R.drawable.stat_notify_sim_toolkit,
285 true, true, null);
286 mShowSafeUnmountNotificationWhenUnmounted = false;
287 } else {
288 setMediaStorageNotification(0, 0, 0, false, false, null);
289 }
290 updateUsbMassStorageNotification(false, false);
291
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700292 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
293 Uri.parse("file://" + path));
294 mContext.sendBroadcast(intent);
295 }
296
297 /**
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800298 * Broadcasts the media checking event to all clients.
299 */
300 void notifyMediaChecking(String path) {
301 setMediaStorageNotification(
302 com.android.internal.R.string.ext_media_checking_notification_title,
303 com.android.internal.R.string.ext_media_checking_notification_message,
304 com.android.internal.R.drawable.stat_notify_sim_toolkit,
305 true, false, null);
306
307 updateUsbMassStorageNotification(true, false);
308 Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING,
309 Uri.parse("file://" + path));
310 mContext.sendBroadcast(intent);
311 }
312
313 /**
314 * Broadcasts the media nofs event to all clients.
315 */
316 void notifyMediaNoFs(String path) {
317
318 Intent intent = new Intent();
319 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
320 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
321
322 setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title,
323 com.android.internal.R.string.ext_media_nofs_notification_message,
324 com.android.internal.R.drawable.stat_sys_no_sim,
325 true, false, pi);
326 updateUsbMassStorageNotification(false, false);
327 intent = new Intent(Intent.ACTION_MEDIA_NOFS,
328 Uri.parse("file://" + path));
329 mContext.sendBroadcast(intent);
330 }
331
332 /**
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700333 * Broadcasts the media mounted event to all clients.
334 */
335 void notifyMediaMounted(String path, boolean readOnly) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800336 setMediaStorageNotification(0, 0, 0, false, false, null);
337 updateUsbMassStorageNotification(false, false);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700338 Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
339 Uri.parse("file://" + path));
340 intent.putExtra("read-only", readOnly);
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800341 mMounted = true;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700342 mContext.sendBroadcast(intent);
343 }
344
345 /**
346 * Broadcasts the media shared event to all clients.
347 */
348 void notifyMediaShared(String path) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800349 Intent intent = new Intent();
350 intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
351 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
352 setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
353 com.android.internal.R.string.usb_storage_stop_notification_message,
354 com.android.internal.R.drawable.stat_sys_warning,
355 false, true, pi);
356 handlePossibleExplicitUnmountBroadcast(path);
357 intent = new Intent(Intent.ACTION_MEDIA_SHARED,
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700358 Uri.parse("file://" + path));
359 mContext.sendBroadcast(intent);
360 }
361
362 /**
363 * Broadcasts the media bad removal event to all clients.
364 */
365 void notifyMediaBadRemoval(String path) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800366 updateUsbMassStorageNotification(true, false);
367 setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title,
368 com.android.internal.R.string.ext_media_badremoval_notification_message,
369 com.android.internal.R.drawable.stat_sys_warning,
370 true, true, null);
371
372 handlePossibleExplicitUnmountBroadcast(path);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700373 Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL,
374 Uri.parse("file://" + path));
375 mContext.sendBroadcast(intent);
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800376
377 intent = new Intent(Intent.ACTION_MEDIA_REMOVED,
378 Uri.parse("file://" + path));
379 mContext.sendBroadcast(intent);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700380 }
381
382 /**
383 * Broadcasts the media unmountable event to all clients.
384 */
385 void notifyMediaUnmountable(String path) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800386 Intent intent = new Intent();
387 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
388 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
389
390 setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title,
391 com.android.internal.R.string.ext_media_unmountable_notification_message,
392 com.android.internal.R.drawable.stat_sys_no_sim,
393 true, false, pi);
394 updateUsbMassStorageNotification(false, false);
395
396 handlePossibleExplicitUnmountBroadcast(path);
397
398 intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE,
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700399 Uri.parse("file://" + path));
400 mContext.sendBroadcast(intent);
401 }
402
403 /**
404 * Broadcasts the media eject event to all clients.
405 */
406 void notifyMediaEject(String path) {
407 Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT,
408 Uri.parse("file://" + path));
409 mContext.sendBroadcast(intent);
410 }
411
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700412 /**
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800413 * Sets the USB storage notification.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700414 */
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800415 private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible,
416 PendingIntent pi) {
417
418 if (!visible && mUsbStorageNotification == null) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700419 return;
420 }
421
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800422 NotificationManager notificationManager = (NotificationManager) mContext
423 .getSystemService(Context.NOTIFICATION_SERVICE);
424
425 if (notificationManager == null) {
426 return;
427 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700428
429 if (visible) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800430 Resources r = Resources.getSystem();
431 CharSequence title = r.getText(titleId);
432 CharSequence message = r.getText(messageId);
433
434 if (mUsbStorageNotification == null) {
435 mUsbStorageNotification = new Notification();
436 mUsbStorageNotification.icon = icon;
437 mUsbStorageNotification.when = 0;
438 }
439
440 if (sound && mPlaySounds) {
441 mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
442 } else {
443 mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
444 }
445
446 mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
447
448 mUsbStorageNotification.tickerText = title;
449 if (pi == null) {
450 Intent intent = new Intent();
451 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
452 }
453
454 mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
455 }
456
457 final int notificationId = mUsbStorageNotification.icon;
458 if (visible) {
459 notificationManager.notify(notificationId, mUsbStorageNotification);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700460 } else {
461 notificationManager.cancel(notificationId);
462 }
463 }
464
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800465 private synchronized boolean getMediaStorageNotificationDismissable() {
466 if ((mMediaStorageNotification != null) &&
467 ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
468 Notification.FLAG_AUTO_CANCEL))
469 return true;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800470
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800471 return false;
472 }
473
474 /**
475 * Sets the media storage notification.
476 */
477 private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
478 boolean dismissable, PendingIntent pi) {
479
480 if (!visible && mMediaStorageNotification == null) {
481 return;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700482 }
483
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800484 NotificationManager notificationManager = (NotificationManager) mContext
485 .getSystemService(Context.NOTIFICATION_SERVICE);
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800486
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800487 if (notificationManager == null) {
488 return;
489 }
490
491 if (mMediaStorageNotification != null && visible) {
492 /*
493 * Dismiss the previous notification - we're about to
494 * re-use it.
495 */
496 final int notificationId = mMediaStorageNotification.icon;
497 notificationManager.cancel(notificationId);
498 }
499
500 if (visible) {
501 Resources r = Resources.getSystem();
502 CharSequence title = r.getText(titleId);
503 CharSequence message = r.getText(messageId);
504
505 if (mMediaStorageNotification == null) {
506 mMediaStorageNotification = new Notification();
507 mMediaStorageNotification.when = 0;
The Android Open Source Project076357b2009-03-03 14:04:24 -0800508 if (mPlaySounds) {
509 mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND;
510 }
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800511 }
512
513 if (dismissable) {
514 mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
515 } else {
516 mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
517 }
518
519 mMediaStorageNotification.tickerText = title;
520 if (pi == null) {
521 Intent intent = new Intent();
522 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
523 }
524
525 mMediaStorageNotification.icon = icon;
526 mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
527 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700528
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800529 final int notificationId = mMediaStorageNotification.icon;
530 if (visible) {
531 notificationManager.notify(notificationId, mMediaStorageNotification);
532 } else {
533 notificationManager.cancel(notificationId);
534 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700535 }
536}
537