blob: 93617d1eee7d055512f968d3b917e1c3af254918 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.PackageManager;
27import android.content.res.Resources;
28import android.net.Uri;
29import android.os.IMountService;
30import android.os.Environment;
31import android.os.RemoteException;
32import android.os.SystemProperties;
33import android.os.UEventObserver;
34import android.text.TextUtils;
35import 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
San Mehat7fd0fee2009-12-17 07:12:23 -080048 class VolumeState {
49 public static final int Init = -1;
50 public static final int NoMedia = 0;
51 public static final int Idle = 1;
52 public static final int Pending = 2;
53 public static final int Checking = 3;
54 public static final int Mounted = 4;
55 public static final int Unmounting = 5;
56 public static final int Formatting = 6;
57 public static final int Shared = 7;
58 public static final int SharedMnt = 8;
59 }
60
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061 /**
62 * Binder context for this service
63 */
64 private Context mContext;
65
66 /**
67 * listener object for communicating with the mount service daemon
68 */
69 private MountListener mListener;
70
71 /**
72 * The notification that is shown when a USB mass storage host
73 * is connected.
74 * <p>
75 * This is lazily created, so use {@link #setUsbStorageNotification()}.
76 */
77 private Notification mUsbStorageNotification;
78
79
80 /**
81 * The notification that is shown when the following media events occur:
82 * - Media is being checked
83 * - Media is blank (or unknown filesystem)
84 * - Media is corrupt
85 * - Media is safe to unmount
86 * - Media is missing
87 * <p>
88 * This is lazily created, so use {@link #setMediaStorageNotification()}.
89 */
90 private Notification mMediaStorageNotification;
91
92 private boolean mShowSafeUnmountNotificationWhenUnmounted;
93
94 private boolean mPlaySounds;
95
96 private boolean mMounted;
97
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -070098 private boolean mAutoStartUms;
99
San Mehat7fd0fee2009-12-17 07:12:23 -0800100 private boolean mUmsConnected = false;
101 private boolean mUmsEnabled = false;
102
103 private String mLegacyState = Environment.MEDIA_REMOVED;
104
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 /**
106 * Constructs a new MountService instance
107 *
108 * @param context Binder context for this service
109 */
110 public MountService(Context context) {
111 mContext = context;
112
113 // Register a BOOT_COMPLETED handler so that we can start
114 // MountListener. We defer the startup so that we don't
115 // start processing events before we ought-to
116 mContext.registerReceiver(mBroadcastReceiver,
117 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
118
119 mListener = new MountListener(this);
120 mShowSafeUnmountNotificationWhenUnmounted = false;
121
122 mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700123
124 mAutoStartUms = SystemProperties.get("persist.service.mount.umsauto", "0").equals("1");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 }
126
127 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
128 public void onReceive(Context context, Intent intent) {
129 if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
130 Thread thread = new Thread(mListener, MountListener.class.getName());
131 thread.start();
132 }
133 }
134 };
135
136 /**
137 * @return true if USB mass storage support is enabled.
138 */
139 public boolean getMassStorageEnabled() throws RemoteException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800140 return mUmsEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 }
142
143 /**
144 * Enables or disables USB mass storage support.
145 *
146 * @param enable true to enable USB mass storage support
147 */
148 public void setMassStorageEnabled(boolean enable) throws RemoteException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800149 try {
150 String vp = Environment.getExternalStorageDirectory().getPath();
151 String vs = getVolumeState(vp);
152
153 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
154 Log.d(TAG, "Unmounting media before UMS enable");
155 unmountMedia(vp);
156 }
157
158 mListener.setShareMethodEnabled(Environment
159 .getExternalStorageDirectory()
160 .getPath(),
161 "ums", enable);
162 mUmsEnabled = enable;
163 if (!enable) {
164 Log.d(TAG, "Mounting media after UMS disable");
165 mountMedia(vp);
166 }
167 } catch (RemoteException rex) {
168 Log.e(TAG, "Failed to set ums enable {" + enable + "}");
169 return;
170 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 }
172
173 /**
174 * @return true if USB mass storage is connected.
175 */
176 public boolean getMassStorageConnected() throws RemoteException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800177 return mUmsConnected;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800179
180 /**
181 * @return state of the volume at the specified mount point
182 */
183 public String getVolumeState(String mountPoint) throws RemoteException {
184 /*
185 * XXX: Until we have multiple volume discovery, just hardwire
186 * this to /sdcard
187 */
188 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
189 Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
190 throw new IllegalArgumentException();
191 }
192
193 return mLegacyState;
194 }
195
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196
197 /**
198 * Attempt to mount external media
199 */
200 public void mountMedia(String mountPath) throws RemoteException {
201 if (mContext.checkCallingOrSelfPermission(
202 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
203 != PackageManager.PERMISSION_GRANTED) {
204 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
205 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800206 mListener.mountVolume(mountPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 }
208
209 /**
210 * Attempt to unmount external media to prepare for eject
211 */
212 public void unmountMedia(String mountPath) throws RemoteException {
213 if (mContext.checkCallingOrSelfPermission(
214 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
215 != PackageManager.PERMISSION_GRANTED) {
216 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
217 }
218
219 // Set a flag so that when we get the unmounted event, we know
220 // to display the notification
221 mShowSafeUnmountNotificationWhenUnmounted = true;
222
223 // tell mountd to unmount the media
San Mehat7fd0fee2009-12-17 07:12:23 -0800224 mListener.unmountVolume(mountPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 }
226
227 /**
228 * Attempt to format external media
229 */
230 public void formatMedia(String formatPath) throws RemoteException {
231 if (mContext.checkCallingOrSelfPermission(
232 android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS)
233 != PackageManager.PERMISSION_GRANTED) {
234 throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
235 }
236
San Mehat7fd0fee2009-12-17 07:12:23 -0800237 mListener.formatVolume(formatPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 }
239
240 /**
241 * Returns true if we're playing media notification sounds.
242 */
243 public boolean getPlayNotificationSounds() {
244 return mPlaySounds;
245 }
246
247 /**
248 * Set whether or not we're playing media notification sounds.
249 */
250 public void setPlayNotificationSounds(boolean enabled) {
251 if (mContext.checkCallingOrSelfPermission(
252 android.Manifest.permission.WRITE_SETTINGS)
253 != PackageManager.PERMISSION_GRANTED) {
254 throw new SecurityException("Requires WRITE_SETTINGS permission");
255 }
256 mPlaySounds = enabled;
257 SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0"));
258 }
259
260 /**
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700261 * Returns true if we auto-start UMS on cable insertion.
262 */
263 public boolean getAutoStartUms() {
264 return mAutoStartUms;
265 }
266
267 /**
268 * Set whether or not we're playing media notification sounds.
269 */
270 public void setAutoStartUms(boolean enabled) {
271 if (mContext.checkCallingOrSelfPermission(
272 android.Manifest.permission.WRITE_SETTINGS)
273 != PackageManager.PERMISSION_GRANTED) {
274 throw new SecurityException("Requires WRITE_SETTINGS permission");
275 }
276 mAutoStartUms = enabled;
277 SystemProperties.set("persist.service.mount.umsauto", (enabled ? "1" : "0"));
278 }
279
San Mehat7fd0fee2009-12-17 07:12:23 -0800280 void updatePublicVolumeState(String mountPoint, String state) {
281 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
282 Log.w(TAG, "Multiple volumes not currently supported");
283 return;
284 }
285 Log.w(TAG, "State for {" + mountPoint + "} = {" + state + "}");
286 mLegacyState = state;
287 }
288
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700289 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 * Update the state of the USB mass storage notification
291 */
292 void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) {
293
294 try {
295
296 if (getMassStorageConnected() && !suppressIfConnected) {
297 Intent intent = new Intent();
298 intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
Mike Lockwood95174432009-08-26 09:44:09 -0700299 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
301 setUsbStorageNotification(
302 com.android.internal.R.string.usb_storage_notification_title,
303 com.android.internal.R.string.usb_storage_notification_message,
304 com.android.internal.R.drawable.stat_sys_data_usb,
305 sound, true, pi);
306 } else {
307 setUsbStorageNotification(0, 0, 0, false, false, null);
308 }
309 } catch (RemoteException e) {
310 // Nothing to do
311 }
312 }
313
314 void handlePossibleExplicitUnmountBroadcast(String path) {
315 if (mMounted) {
316 mMounted = false;
317 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
318 Uri.parse("file://" + path));
319 mContext.sendBroadcast(intent);
320 }
321 }
322
San Mehat7fd0fee2009-12-17 07:12:23 -0800323 void onVoldConnected() {
324 new Thread() {
325 public void run() {
326 try {
327 if (!getVolumeState(Environment.getExternalStorageDirectory().getPath())
328 .equals(Environment.MEDIA_MOUNTED)) {
329 try {
330 mountMedia(Environment.getExternalStorageDirectory().getPath());
331 Log.d(TAG, "Connection-mount suceeded");
332 } catch (Exception ex) {
333 Log.w(TAG, "Connection-mount failed");
334 }
335 } else {
336 Log.d(TAG, "Skipping connection-mount; already mounted");
337 }
338 } catch (RemoteException rex) {
339 Log.e(TAG, "Exception while handling connection mount " + rex);
340 }
341
342 try {
343 boolean avail = mListener.getShareAvailable("ums");
344 notifyShareAvailabilityChange("ums", avail);
345 } catch (Exception ex) {
346 Log.w(TAG, "Failed to get share availability");
347 }
348 }
349 }.start();
350 }
351
352 void notifyVolumeStateChange(String label, String mountPoint, int oldState,
353 int newState) throws RemoteException {
354 String vs = getVolumeState(mountPoint);
355
356 if (newState == VolumeState.Init) {
357 } else if (newState == VolumeState.NoMedia) {
358 // NoMedia is handled via Disk Remove events
359 } else if (newState == VolumeState.Idle) {
360 // Don't notify if we're in BAD_REMOVAL, NOFS, or UNMOUNTABLE
361 if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) &&
362 !vs.equals(Environment.MEDIA_NOFS) &&
363 !vs.equals(Environment.MEDIA_UNMOUNTABLE)) {
364 notifyMediaUnmounted(mountPoint);
365 }
366 } else if (newState == VolumeState.Pending) {
367 } else if (newState == VolumeState.Checking) {
368 notifyMediaChecking(mountPoint);
369 } else if (newState == VolumeState.Mounted) {
370 notifyMediaMounted(mountPoint, false);
371 } else if (newState == VolumeState.Unmounting) {
372 notifyMediaUnmounting(mountPoint);
373 } else if (newState == VolumeState.Formatting) {
374 } else if (newState == VolumeState.Shared) {
375 notifyMediaShared(mountPoint, false);
376 } else if (newState == VolumeState.SharedMnt) {
377 notifyMediaShared(mountPoint, true);
378 } else {
379 Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
380 }
381 }
382
383
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 /**
385 * Broadcasts the USB mass storage connected event to all clients.
386 */
387 void notifyUmsConnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800388 mUmsConnected = true;
389
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 String storageState = Environment.getExternalStorageState();
391 if (!storageState.equals(Environment.MEDIA_REMOVED) &&
392 !storageState.equals(Environment.MEDIA_BAD_REMOVAL) &&
393 !storageState.equals(Environment.MEDIA_CHECKING)) {
394
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700395 if (mAutoStartUms) {
396 try {
397 setMassStorageEnabled(true);
398 } catch (RemoteException e) {
399 }
400 } else {
401 updateUsbMassStorageNotification(false, true);
402 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 }
404
405 Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
406 mContext.sendBroadcast(intent);
407 }
408
San Mehat7fd0fee2009-12-17 07:12:23 -0800409 void notifyShareAvailabilityChange(String method, boolean avail) {
410 Log.d(TAG, "Share method {" + method + "} availability now " + avail);
411 if (!method.equals("ums")) {
412 Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
413 return;
414 }
415 if (avail) {
416 notifyUmsConnected();
417 } else {
418 notifyUmsDisconnected();
419 }
420 }
421
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 /**
423 * Broadcasts the USB mass storage disconnected event to all clients.
424 */
425 void notifyUmsDisconnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800426 mUmsConnected = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 updateUsbMassStorageNotification(false, false);
428 Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
429 mContext.sendBroadcast(intent);
430 }
431
San Mehat7fd0fee2009-12-17 07:12:23 -0800432 void notifyMediaInserted(final String path) throws RemoteException {
433 new Thread() {
434 public void run() {
435 try {
436 Log.d(TAG, "Mounting media after insertion");
437 mountMedia(path);
438 } catch (Exception ex) {
439 Log.w(TAG, "Failed to mount media on insertion");
440 }
441 }
442 }.start();
443 }
444
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 /**
446 * Broadcasts the media removed event to all clients.
447 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800448 void notifyMediaRemoved(String path) throws RemoteException {
449
450 // Suppress this on bad removal
451 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
452 return;
453 }
454
455 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
456
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 updateUsbMassStorageNotification(true, false);
458
459 setMediaStorageNotification(
San Mehat7fd0fee2009-12-17 07:12:23 -0800460 com.android.internal.R.string.ext_media_nomedia_notification_title,
461 com.android.internal.R.string.ext_media_nomedia_notification_message,
462 com.android.internal.R.drawable.stat_notify_sdcard_usb,
463 true, false, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 handlePossibleExplicitUnmountBroadcast(path);
465
San Mehat7fd0fee2009-12-17 07:12:23 -0800466 // Log.d(TAG, "Sending ACTION_MEDIA_REMOVED");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED,
468 Uri.parse("file://" + path));
469 mContext.sendBroadcast(intent);
470 }
471
472 /**
473 * Broadcasts the media unmounted event to all clients.
474 */
475 void notifyMediaUnmounted(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800476
477 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
478
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 if (mShowSafeUnmountNotificationWhenUnmounted) {
480 setMediaStorageNotification(
481 com.android.internal.R.string.ext_media_safe_unmount_notification_title,
482 com.android.internal.R.string.ext_media_safe_unmount_notification_message,
Mike Lockwoodde46acd2009-09-30 19:30:56 -0400483 com.android.internal.R.drawable.stat_notify_sdcard,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 true, true, null);
485 mShowSafeUnmountNotificationWhenUnmounted = false;
486 } else {
487 setMediaStorageNotification(0, 0, 0, false, false, null);
488 }
489 updateUsbMassStorageNotification(false, false);
490
San Mehat7fd0fee2009-12-17 07:12:23 -0800491 // Log.d(TAG, "Sending ACTION_MEDIA_UNMOUNTED");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
493 Uri.parse("file://" + path));
494 mContext.sendBroadcast(intent);
495 }
496
497 /**
498 * Broadcasts the media checking event to all clients.
499 */
500 void notifyMediaChecking(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800501 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
502
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 setMediaStorageNotification(
504 com.android.internal.R.string.ext_media_checking_notification_title,
505 com.android.internal.R.string.ext_media_checking_notification_message,
Mike Lockwoodde46acd2009-09-30 19:30:56 -0400506 com.android.internal.R.drawable.stat_notify_sdcard_prepare,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 true, false, null);
508
509 updateUsbMassStorageNotification(true, false);
San Mehat7fd0fee2009-12-17 07:12:23 -0800510 // Log.d(TAG, "Sending ACTION_MEDIA_CHECKING");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING,
512 Uri.parse("file://" + path));
513 mContext.sendBroadcast(intent);
514 }
515
516 /**
517 * Broadcasts the media nofs event to all clients.
518 */
519 void notifyMediaNoFs(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800520 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521
522 Intent intent = new Intent();
523 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
524 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
525
526 setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title,
527 com.android.internal.R.string.ext_media_nofs_notification_message,
Mike Lockwooda7ef2692009-09-10 11:15:26 -0400528 com.android.internal.R.drawable.stat_notify_sdcard_usb,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 true, false, pi);
530 updateUsbMassStorageNotification(false, false);
San Mehat7fd0fee2009-12-17 07:12:23 -0800531 // Log.d(TAG, "Sending ACTION_MEDIA_NOFS");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 intent = new Intent(Intent.ACTION_MEDIA_NOFS,
533 Uri.parse("file://" + path));
534 mContext.sendBroadcast(intent);
535 }
536
537 /**
538 * Broadcasts the media mounted event to all clients.
539 */
540 void notifyMediaMounted(String path, boolean readOnly) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800541 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
542
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 setMediaStorageNotification(0, 0, 0, false, false, null);
544 updateUsbMassStorageNotification(false, false);
San Mehat7fd0fee2009-12-17 07:12:23 -0800545 // Log.d(TAG, "Sending ACTION_MEDIA_MOUNTED");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
547 Uri.parse("file://" + path));
548 intent.putExtra("read-only", readOnly);
549 mMounted = true;
550 mContext.sendBroadcast(intent);
551 }
552
553 /**
554 * Broadcasts the media shared event to all clients.
555 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800556 void notifyMediaShared(String path, boolean mounted) {
557 if (mounted) {
558 Log.e(TAG, "Live shared mounts not supported yet!");
559 return;
560 }
561
562 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
563
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 Intent intent = new Intent();
565 intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
566 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
567 setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
568 com.android.internal.R.string.usb_storage_stop_notification_message,
569 com.android.internal.R.drawable.stat_sys_warning,
570 false, true, pi);
571 handlePossibleExplicitUnmountBroadcast(path);
San Mehat7fd0fee2009-12-17 07:12:23 -0800572 // Log.d(TAG, "Sending ACTION_MEDIA_SHARED");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 intent = new Intent(Intent.ACTION_MEDIA_SHARED,
574 Uri.parse("file://" + path));
575 mContext.sendBroadcast(intent);
576 }
577
578 /**
579 * Broadcasts the media bad removal event to all clients.
580 */
581 void notifyMediaBadRemoval(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800582 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
583
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 updateUsbMassStorageNotification(true, false);
585 setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title,
586 com.android.internal.R.string.ext_media_badremoval_notification_message,
587 com.android.internal.R.drawable.stat_sys_warning,
588 true, true, null);
589
590 handlePossibleExplicitUnmountBroadcast(path);
San Mehat7fd0fee2009-12-17 07:12:23 -0800591 // Log.d(TAG, "Sending ACTION_MEDIA_BAD_REMOVAL");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL,
593 Uri.parse("file://" + path));
594 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 }
596
597 /**
598 * Broadcasts the media unmountable event to all clients.
599 */
600 void notifyMediaUnmountable(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800601 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
602
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 Intent intent = new Intent();
604 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
605 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
606
607 setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title,
608 com.android.internal.R.string.ext_media_unmountable_notification_message,
Mike Lockwooda7ef2692009-09-10 11:15:26 -0400609 com.android.internal.R.drawable.stat_notify_sdcard_usb,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 true, false, pi);
611 updateUsbMassStorageNotification(false, false);
612
613 handlePossibleExplicitUnmountBroadcast(path);
614
San Mehat7fd0fee2009-12-17 07:12:23 -0800615 // Log.d(TAG, "Sending ACTION_MEDIA_UNMOUNTABLE");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE,
617 Uri.parse("file://" + path));
618 mContext.sendBroadcast(intent);
619 }
620
621 /**
622 * Broadcasts the media eject event to all clients.
623 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800624 void notifyMediaUnmounting(String path) {
625 // Log.d(TAG, "Sending ACTION_MEDIA_EJECT");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626 Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT,
627 Uri.parse("file://" + path));
628 mContext.sendBroadcast(intent);
629 }
630
631 /**
632 * Sets the USB storage notification.
633 */
634 private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible,
635 PendingIntent pi) {
636
637 if (!visible && mUsbStorageNotification == null) {
638 return;
639 }
640
641 NotificationManager notificationManager = (NotificationManager) mContext
642 .getSystemService(Context.NOTIFICATION_SERVICE);
643
644 if (notificationManager == null) {
645 return;
646 }
647
648 if (visible) {
649 Resources r = Resources.getSystem();
650 CharSequence title = r.getText(titleId);
651 CharSequence message = r.getText(messageId);
652
653 if (mUsbStorageNotification == null) {
654 mUsbStorageNotification = new Notification();
655 mUsbStorageNotification.icon = icon;
656 mUsbStorageNotification.when = 0;
657 }
658
659 if (sound && mPlaySounds) {
660 mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
661 } else {
662 mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
663 }
664
665 mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
666
667 mUsbStorageNotification.tickerText = title;
668 if (pi == null) {
669 Intent intent = new Intent();
670 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
671 }
672
673 mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
674 }
675
676 final int notificationId = mUsbStorageNotification.icon;
677 if (visible) {
678 notificationManager.notify(notificationId, mUsbStorageNotification);
679 } else {
680 notificationManager.cancel(notificationId);
681 }
682 }
683
684 private synchronized boolean getMediaStorageNotificationDismissable() {
685 if ((mMediaStorageNotification != null) &&
686 ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
687 Notification.FLAG_AUTO_CANCEL))
688 return true;
689
690 return false;
691 }
692
693 /**
694 * Sets the media storage notification.
695 */
696 private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
697 boolean dismissable, PendingIntent pi) {
698
699 if (!visible && mMediaStorageNotification == null) {
700 return;
701 }
702
703 NotificationManager notificationManager = (NotificationManager) mContext
704 .getSystemService(Context.NOTIFICATION_SERVICE);
705
706 if (notificationManager == null) {
707 return;
708 }
709
710 if (mMediaStorageNotification != null && visible) {
711 /*
712 * Dismiss the previous notification - we're about to
713 * re-use it.
714 */
715 final int notificationId = mMediaStorageNotification.icon;
716 notificationManager.cancel(notificationId);
717 }
718
719 if (visible) {
720 Resources r = Resources.getSystem();
721 CharSequence title = r.getText(titleId);
722 CharSequence message = r.getText(messageId);
723
724 if (mMediaStorageNotification == null) {
725 mMediaStorageNotification = new Notification();
726 mMediaStorageNotification.when = 0;
727 }
728
729 if (mPlaySounds) {
730 mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND;
731 } else {
732 mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
733 }
734
735 if (dismissable) {
736 mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
737 } else {
738 mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
739 }
740
741 mMediaStorageNotification.tickerText = title;
742 if (pi == null) {
743 Intent intent = new Intent();
744 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
745 }
746
747 mMediaStorageNotification.icon = icon;
748 mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
749 }
750
751 final int notificationId = mMediaStorageNotification.icon;
752 if (visible) {
753 notificationManager.notify(notificationId, mMediaStorageNotification);
754 } else {
755 notificationManager.cancel(notificationId);
756 }
757 }
758}
759