blob: 386dc0a175cdab90d278f19386ac3711aa58762b [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.os.SystemProperties;
32import android.os.UEventObserver;
33import android.text.TextUtils;
34import android.util.Log;
35
36import java.io.File;
37import java.io.FileReader;
San Mehat36972292010-01-06 11:06:32 -080038import java.lang.IllegalStateException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039
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);
San Mehat91c77612010-01-07 10:39:41 -0800118 mContext.registerReceiver(mBroadcastReceiver,
119 new IntentFilter(Intent.ACTION_SHUTDOWN), null, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120
121 mListener = new MountListener(this);
122 mShowSafeUnmountNotificationWhenUnmounted = false;
123
124 mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700125
126 mAutoStartUms = SystemProperties.get("persist.service.mount.umsauto", "0").equals("1");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 }
128
129 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
130 public void onReceive(Context context, Intent intent) {
San Mehat91c77612010-01-07 10:39:41 -0800131 String action = intent.getAction();
132
133 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 Thread thread = new Thread(mListener, MountListener.class.getName());
135 thread.start();
San Mehat91c77612010-01-07 10:39:41 -0800136 } else if (action.equals(Intent.ACTION_SHUTDOWN)) {
137 shutdown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 }
139 }
140 };
141
San Mehat91c77612010-01-07 10:39:41 -0800142 public void shutdown() {
143 if (mContext.checkCallingOrSelfPermission(
144 android.Manifest.permission.SHUTDOWN)
145 != PackageManager.PERMISSION_GRANTED) {
146 throw new SecurityException("Requires SHUTDOWN permission");
147 }
148
149 Log.d(TAG, "Shutting down");
150 String state = Environment.getExternalStorageState();
151
152 if (state.equals(Environment.MEDIA_SHARED)) {
153 /*
154 * If the media is currently shared, unshare it.
155 * XXX: This is still dangerous!. We should not
156 * be rebooting at *all* if UMS is enabled, since
157 * the UMS host could have dirty FAT cache entries
158 * yet to flush.
159 */
160 try {
161 setMassStorageEnabled(false);
162 } catch (Exception e) {
163 Log.e(TAG, "ums disable failed", e);
164 }
165 } else if (state.equals(Environment.MEDIA_CHECKING)) {
166 /*
167 * If the media is being checked, then we need to wait for
168 * it to complete before being able to proceed.
169 */
170 // XXX: @hackbod - Should we disable the ANR timer here?
171 int retries = 30;
172 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
173 try {
174 Thread.sleep(1000);
175 } catch (InterruptedException iex) {
176 Log.e(TAG, "Interrupted while waiting for media", iex);
177 break;
178 }
179 state = Environment.getExternalStorageState();
180 }
181 if (retries == 0) {
182 Log.e(TAG, "Timed out waiting for media to check");
183 }
184 }
185
186 if (state.equals(Environment.MEDIA_MOUNTED)) {
187 /*
188 * If the media is mounted, then gracefully unmount it.
189 */
190 try {
191 String m = Environment.getExternalStorageDirectory().toString();
192 unmountMedia(m);
193 } catch (Exception e) {
194 Log.e(TAG, "external storage unmount failed", e);
195 }
196 }
197 }
198
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 /**
200 * @return true if USB mass storage support is enabled.
201 */
San Mehat36972292010-01-06 11:06:32 -0800202 public boolean getMassStorageEnabled() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800203 return mUmsEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 }
205
206 /**
207 * Enables or disables USB mass storage support.
208 *
209 * @param enable true to enable USB mass storage support
210 */
San Mehat36972292010-01-06 11:06:32 -0800211 public void setMassStorageEnabled(boolean enable) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800212 try {
213 String vp = Environment.getExternalStorageDirectory().getPath();
214 String vs = getVolumeState(vp);
215
216 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
217 Log.d(TAG, "Unmounting media before UMS enable");
218 unmountMedia(vp);
219 }
220
221 mListener.setShareMethodEnabled(Environment
222 .getExternalStorageDirectory()
223 .getPath(),
224 "ums", enable);
225 mUmsEnabled = enable;
226 if (!enable) {
227 Log.d(TAG, "Mounting media after UMS disable");
228 mountMedia(vp);
229 }
San Mehat36972292010-01-06 11:06:32 -0800230 } catch (IllegalStateException rex) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800231 Log.e(TAG, "Failed to set ums enable {" + enable + "}");
232 return;
233 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 }
235
236 /**
237 * @return true if USB mass storage is connected.
238 */
San Mehat36972292010-01-06 11:06:32 -0800239 public boolean getMassStorageConnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800240 return mUmsConnected;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800242
243 /**
244 * @return state of the volume at the specified mount point
245 */
San Mehat36972292010-01-06 11:06:32 -0800246 public String getVolumeState(String mountPoint) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800247 /*
248 * XXX: Until we have multiple volume discovery, just hardwire
249 * this to /sdcard
250 */
251 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
252 Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
253 throw new IllegalArgumentException();
254 }
255
256 return mLegacyState;
257 }
258
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259
260 /**
261 * Attempt to mount external media
262 */
San Mehat36972292010-01-06 11:06:32 -0800263 public void mountMedia(String mountPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 if (mContext.checkCallingOrSelfPermission(
265 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
266 != PackageManager.PERMISSION_GRANTED) {
267 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
268 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800269 mListener.mountVolume(mountPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 }
271
272 /**
273 * Attempt to unmount external media to prepare for eject
274 */
San Mehat36972292010-01-06 11:06:32 -0800275 public void unmountMedia(String mountPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 if (mContext.checkCallingOrSelfPermission(
277 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
278 != PackageManager.PERMISSION_GRANTED) {
279 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
280 }
281
282 // Set a flag so that when we get the unmounted event, we know
283 // to display the notification
284 mShowSafeUnmountNotificationWhenUnmounted = true;
285
286 // tell mountd to unmount the media
San Mehat7fd0fee2009-12-17 07:12:23 -0800287 mListener.unmountVolume(mountPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 }
289
290 /**
291 * Attempt to format external media
292 */
San Mehat36972292010-01-06 11:06:32 -0800293 public void formatMedia(String formatPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 if (mContext.checkCallingOrSelfPermission(
295 android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS)
296 != PackageManager.PERMISSION_GRANTED) {
297 throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
298 }
299
San Mehat7fd0fee2009-12-17 07:12:23 -0800300 mListener.formatVolume(formatPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 }
302
303 /**
304 * Returns true if we're playing media notification sounds.
305 */
306 public boolean getPlayNotificationSounds() {
307 return mPlaySounds;
308 }
309
310 /**
311 * Set whether or not we're playing media notification sounds.
312 */
313 public void setPlayNotificationSounds(boolean enabled) {
314 if (mContext.checkCallingOrSelfPermission(
315 android.Manifest.permission.WRITE_SETTINGS)
316 != PackageManager.PERMISSION_GRANTED) {
317 throw new SecurityException("Requires WRITE_SETTINGS permission");
318 }
319 mPlaySounds = enabled;
320 SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0"));
321 }
322
323 /**
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700324 * Returns true if we auto-start UMS on cable insertion.
325 */
326 public boolean getAutoStartUms() {
327 return mAutoStartUms;
328 }
329
330 /**
331 * Set whether or not we're playing media notification sounds.
332 */
333 public void setAutoStartUms(boolean enabled) {
334 if (mContext.checkCallingOrSelfPermission(
335 android.Manifest.permission.WRITE_SETTINGS)
336 != PackageManager.PERMISSION_GRANTED) {
337 throw new SecurityException("Requires WRITE_SETTINGS permission");
338 }
339 mAutoStartUms = enabled;
340 SystemProperties.set("persist.service.mount.umsauto", (enabled ? "1" : "0"));
341 }
342
San Mehat7fd0fee2009-12-17 07:12:23 -0800343 void updatePublicVolumeState(String mountPoint, String state) {
344 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
345 Log.w(TAG, "Multiple volumes not currently supported");
346 return;
347 }
348 Log.w(TAG, "State for {" + mountPoint + "} = {" + state + "}");
349 mLegacyState = state;
350 }
351
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700352 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 * Update the state of the USB mass storage notification
354 */
355 void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) {
356
357 try {
358
359 if (getMassStorageConnected() && !suppressIfConnected) {
360 Intent intent = new Intent();
361 intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
Mike Lockwood95174432009-08-26 09:44:09 -0700362 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
364 setUsbStorageNotification(
365 com.android.internal.R.string.usb_storage_notification_title,
366 com.android.internal.R.string.usb_storage_notification_message,
367 com.android.internal.R.drawable.stat_sys_data_usb,
368 sound, true, pi);
369 } else {
370 setUsbStorageNotification(0, 0, 0, false, false, null);
371 }
San Mehat36972292010-01-06 11:06:32 -0800372 } catch (IllegalStateException e) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 // Nothing to do
374 }
375 }
376
377 void handlePossibleExplicitUnmountBroadcast(String path) {
378 if (mMounted) {
379 mMounted = false;
380 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
381 Uri.parse("file://" + path));
382 mContext.sendBroadcast(intent);
383 }
384 }
385
San Mehat7fd0fee2009-12-17 07:12:23 -0800386 void onVoldConnected() {
387 new Thread() {
388 public void run() {
389 try {
390 if (!getVolumeState(Environment.getExternalStorageDirectory().getPath())
391 .equals(Environment.MEDIA_MOUNTED)) {
392 try {
393 mountMedia(Environment.getExternalStorageDirectory().getPath());
394 Log.d(TAG, "Connection-mount suceeded");
395 } catch (Exception ex) {
396 Log.w(TAG, "Connection-mount failed");
397 }
398 } else {
399 Log.d(TAG, "Skipping connection-mount; already mounted");
400 }
San Mehat36972292010-01-06 11:06:32 -0800401 } catch (IllegalStateException rex) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800402 Log.e(TAG, "Exception while handling connection mount " + rex);
403 }
404
405 try {
406 boolean avail = mListener.getShareAvailable("ums");
407 notifyShareAvailabilityChange("ums", avail);
408 } catch (Exception ex) {
409 Log.w(TAG, "Failed to get share availability");
410 }
411 }
412 }.start();
413 }
414
415 void notifyVolumeStateChange(String label, String mountPoint, int oldState,
San Mehat36972292010-01-06 11:06:32 -0800416 int newState) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800417 String vs = getVolumeState(mountPoint);
418
419 if (newState == VolumeState.Init) {
420 } else if (newState == VolumeState.NoMedia) {
421 // NoMedia is handled via Disk Remove events
422 } else if (newState == VolumeState.Idle) {
423 // Don't notify if we're in BAD_REMOVAL, NOFS, or UNMOUNTABLE
424 if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) &&
425 !vs.equals(Environment.MEDIA_NOFS) &&
426 !vs.equals(Environment.MEDIA_UNMOUNTABLE)) {
427 notifyMediaUnmounted(mountPoint);
428 }
429 } else if (newState == VolumeState.Pending) {
430 } else if (newState == VolumeState.Checking) {
431 notifyMediaChecking(mountPoint);
432 } else if (newState == VolumeState.Mounted) {
433 notifyMediaMounted(mountPoint, false);
434 } else if (newState == VolumeState.Unmounting) {
435 notifyMediaUnmounting(mountPoint);
436 } else if (newState == VolumeState.Formatting) {
437 } else if (newState == VolumeState.Shared) {
438 notifyMediaShared(mountPoint, false);
439 } else if (newState == VolumeState.SharedMnt) {
440 notifyMediaShared(mountPoint, true);
441 } else {
442 Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
443 }
444 }
445
446
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 /**
448 * Broadcasts the USB mass storage connected event to all clients.
449 */
450 void notifyUmsConnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800451 mUmsConnected = true;
452
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 String storageState = Environment.getExternalStorageState();
454 if (!storageState.equals(Environment.MEDIA_REMOVED) &&
455 !storageState.equals(Environment.MEDIA_BAD_REMOVAL) &&
456 !storageState.equals(Environment.MEDIA_CHECKING)) {
457
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700458 if (mAutoStartUms) {
459 try {
460 setMassStorageEnabled(true);
San Mehat36972292010-01-06 11:06:32 -0800461 } catch (IllegalStateException e) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700462 }
463 } else {
464 updateUsbMassStorageNotification(false, true);
465 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 }
467
468 Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
469 mContext.sendBroadcast(intent);
470 }
471
San Mehat7fd0fee2009-12-17 07:12:23 -0800472 void notifyShareAvailabilityChange(String method, boolean avail) {
473 Log.d(TAG, "Share method {" + method + "} availability now " + avail);
474 if (!method.equals("ums")) {
475 Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
476 return;
477 }
478 if (avail) {
479 notifyUmsConnected();
480 } else {
481 notifyUmsDisconnected();
482 }
483 }
484
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 /**
486 * Broadcasts the USB mass storage disconnected event to all clients.
487 */
488 void notifyUmsDisconnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800489 mUmsConnected = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 updateUsbMassStorageNotification(false, false);
491 Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
492 mContext.sendBroadcast(intent);
493 }
494
San Mehat36972292010-01-06 11:06:32 -0800495 void notifyMediaInserted(final String path) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800496 new Thread() {
497 public void run() {
498 try {
499 Log.d(TAG, "Mounting media after insertion");
500 mountMedia(path);
501 } catch (Exception ex) {
502 Log.w(TAG, "Failed to mount media on insertion");
503 }
504 }
505 }.start();
506 }
507
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 /**
509 * Broadcasts the media removed event to all clients.
510 */
San Mehat36972292010-01-06 11:06:32 -0800511 void notifyMediaRemoved(String path) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800512
513 // Suppress this on bad removal
514 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
515 return;
516 }
517
518 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
519
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 updateUsbMassStorageNotification(true, false);
521
522 setMediaStorageNotification(
San Mehat7fd0fee2009-12-17 07:12:23 -0800523 com.android.internal.R.string.ext_media_nomedia_notification_title,
524 com.android.internal.R.string.ext_media_nomedia_notification_message,
525 com.android.internal.R.drawable.stat_notify_sdcard_usb,
526 true, false, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 handlePossibleExplicitUnmountBroadcast(path);
528
San Mehat7fd0fee2009-12-17 07:12:23 -0800529 // Log.d(TAG, "Sending ACTION_MEDIA_REMOVED");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED,
531 Uri.parse("file://" + path));
532 mContext.sendBroadcast(intent);
533 }
534
535 /**
536 * Broadcasts the media unmounted event to all clients.
537 */
538 void notifyMediaUnmounted(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800539
540 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
541
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 if (mShowSafeUnmountNotificationWhenUnmounted) {
543 setMediaStorageNotification(
544 com.android.internal.R.string.ext_media_safe_unmount_notification_title,
545 com.android.internal.R.string.ext_media_safe_unmount_notification_message,
Mike Lockwoodde46acdd2009-09-30 19:30:56 -0400546 com.android.internal.R.drawable.stat_notify_sdcard,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 true, true, null);
548 mShowSafeUnmountNotificationWhenUnmounted = false;
549 } else {
550 setMediaStorageNotification(0, 0, 0, false, false, null);
551 }
552 updateUsbMassStorageNotification(false, false);
553
San Mehat7fd0fee2009-12-17 07:12:23 -0800554 // Log.d(TAG, "Sending ACTION_MEDIA_UNMOUNTED");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
556 Uri.parse("file://" + path));
557 mContext.sendBroadcast(intent);
558 }
559
560 /**
561 * Broadcasts the media checking event to all clients.
562 */
563 void notifyMediaChecking(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800564 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
565
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 setMediaStorageNotification(
567 com.android.internal.R.string.ext_media_checking_notification_title,
568 com.android.internal.R.string.ext_media_checking_notification_message,
Mike Lockwoodde46acdd2009-09-30 19:30:56 -0400569 com.android.internal.R.drawable.stat_notify_sdcard_prepare,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 true, false, null);
571
572 updateUsbMassStorageNotification(true, false);
San Mehat7fd0fee2009-12-17 07:12:23 -0800573 // Log.d(TAG, "Sending ACTION_MEDIA_CHECKING");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING,
575 Uri.parse("file://" + path));
576 mContext.sendBroadcast(intent);
577 }
578
579 /**
580 * Broadcasts the media nofs event to all clients.
581 */
582 void notifyMediaNoFs(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800583 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584
585 Intent intent = new Intent();
586 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
587 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
588
589 setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title,
590 com.android.internal.R.string.ext_media_nofs_notification_message,
Mike Lockwooda7ef2692009-09-10 11:15:26 -0400591 com.android.internal.R.drawable.stat_notify_sdcard_usb,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 true, false, pi);
593 updateUsbMassStorageNotification(false, false);
San Mehat7fd0fee2009-12-17 07:12:23 -0800594 // Log.d(TAG, "Sending ACTION_MEDIA_NOFS");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 intent = new Intent(Intent.ACTION_MEDIA_NOFS,
596 Uri.parse("file://" + path));
597 mContext.sendBroadcast(intent);
598 }
599
600 /**
601 * Broadcasts the media mounted event to all clients.
602 */
603 void notifyMediaMounted(String path, boolean readOnly) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800604 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
605
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606 setMediaStorageNotification(0, 0, 0, false, false, null);
607 updateUsbMassStorageNotification(false, false);
San Mehat7fd0fee2009-12-17 07:12:23 -0800608 // Log.d(TAG, "Sending ACTION_MEDIA_MOUNTED");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
610 Uri.parse("file://" + path));
611 intent.putExtra("read-only", readOnly);
612 mMounted = true;
613 mContext.sendBroadcast(intent);
614 }
615
616 /**
617 * Broadcasts the media shared event to all clients.
618 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800619 void notifyMediaShared(String path, boolean mounted) {
620 if (mounted) {
621 Log.e(TAG, "Live shared mounts not supported yet!");
622 return;
623 }
624
625 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
626
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 Intent intent = new Intent();
628 intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
629 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
630 setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
631 com.android.internal.R.string.usb_storage_stop_notification_message,
632 com.android.internal.R.drawable.stat_sys_warning,
633 false, true, pi);
634 handlePossibleExplicitUnmountBroadcast(path);
San Mehat7fd0fee2009-12-17 07:12:23 -0800635 // Log.d(TAG, "Sending ACTION_MEDIA_SHARED");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 intent = new Intent(Intent.ACTION_MEDIA_SHARED,
637 Uri.parse("file://" + path));
638 mContext.sendBroadcast(intent);
639 }
640
641 /**
642 * Broadcasts the media bad removal event to all clients.
643 */
644 void notifyMediaBadRemoval(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800645 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
646
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 updateUsbMassStorageNotification(true, false);
648 setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title,
649 com.android.internal.R.string.ext_media_badremoval_notification_message,
650 com.android.internal.R.drawable.stat_sys_warning,
651 true, true, null);
652
653 handlePossibleExplicitUnmountBroadcast(path);
San Mehat7fd0fee2009-12-17 07:12:23 -0800654 // Log.d(TAG, "Sending ACTION_MEDIA_BAD_REMOVAL");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800655 Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL,
656 Uri.parse("file://" + path));
657 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 }
659
660 /**
661 * Broadcasts the media unmountable event to all clients.
662 */
663 void notifyMediaUnmountable(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800664 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
665
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 Intent intent = new Intent();
667 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
668 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
669
670 setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title,
671 com.android.internal.R.string.ext_media_unmountable_notification_message,
Mike Lockwooda7ef2692009-09-10 11:15:26 -0400672 com.android.internal.R.drawable.stat_notify_sdcard_usb,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 true, false, pi);
674 updateUsbMassStorageNotification(false, false);
675
676 handlePossibleExplicitUnmountBroadcast(path);
677
San Mehat7fd0fee2009-12-17 07:12:23 -0800678 // Log.d(TAG, "Sending ACTION_MEDIA_UNMOUNTABLE");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE,
680 Uri.parse("file://" + path));
681 mContext.sendBroadcast(intent);
682 }
683
684 /**
685 * Broadcasts the media eject event to all clients.
686 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800687 void notifyMediaUnmounting(String path) {
688 // Log.d(TAG, "Sending ACTION_MEDIA_EJECT");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT,
690 Uri.parse("file://" + path));
691 mContext.sendBroadcast(intent);
692 }
693
694 /**
695 * Sets the USB storage notification.
696 */
697 private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible,
698 PendingIntent pi) {
699
700 if (!visible && mUsbStorageNotification == null) {
701 return;
702 }
703
704 NotificationManager notificationManager = (NotificationManager) mContext
705 .getSystemService(Context.NOTIFICATION_SERVICE);
706
707 if (notificationManager == null) {
708 return;
709 }
710
711 if (visible) {
712 Resources r = Resources.getSystem();
713 CharSequence title = r.getText(titleId);
714 CharSequence message = r.getText(messageId);
715
716 if (mUsbStorageNotification == null) {
717 mUsbStorageNotification = new Notification();
718 mUsbStorageNotification.icon = icon;
719 mUsbStorageNotification.when = 0;
720 }
721
722 if (sound && mPlaySounds) {
723 mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
724 } else {
725 mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
726 }
727
728 mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
729
730 mUsbStorageNotification.tickerText = title;
731 if (pi == null) {
732 Intent intent = new Intent();
733 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
734 }
735
736 mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
737 }
738
739 final int notificationId = mUsbStorageNotification.icon;
740 if (visible) {
741 notificationManager.notify(notificationId, mUsbStorageNotification);
742 } else {
743 notificationManager.cancel(notificationId);
744 }
745 }
746
747 private synchronized boolean getMediaStorageNotificationDismissable() {
748 if ((mMediaStorageNotification != null) &&
749 ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
750 Notification.FLAG_AUTO_CANCEL))
751 return true;
752
753 return false;
754 }
755
756 /**
757 * Sets the media storage notification.
758 */
759 private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
760 boolean dismissable, PendingIntent pi) {
761
762 if (!visible && mMediaStorageNotification == null) {
763 return;
764 }
765
766 NotificationManager notificationManager = (NotificationManager) mContext
767 .getSystemService(Context.NOTIFICATION_SERVICE);
768
769 if (notificationManager == null) {
770 return;
771 }
772
773 if (mMediaStorageNotification != null && visible) {
774 /*
775 * Dismiss the previous notification - we're about to
776 * re-use it.
777 */
778 final int notificationId = mMediaStorageNotification.icon;
779 notificationManager.cancel(notificationId);
780 }
781
782 if (visible) {
783 Resources r = Resources.getSystem();
784 CharSequence title = r.getText(titleId);
785 CharSequence message = r.getText(messageId);
786
787 if (mMediaStorageNotification == null) {
788 mMediaStorageNotification = new Notification();
789 mMediaStorageNotification.when = 0;
790 }
791
792 if (mPlaySounds) {
793 mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND;
794 } else {
795 mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
796 }
797
798 if (dismissable) {
799 mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
800 } else {
801 mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
802 }
803
804 mMediaStorageNotification.tickerText = title;
805 if (pi == null) {
806 Intent intent = new Intent();
807 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
808 }
809
810 mMediaStorageNotification.icon = icon;
811 mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
812 }
813
814 final int notificationId = mMediaStorageNotification.icon;
815 if (visible) {
816 notificationManager.notify(notificationId, mMediaStorageNotification);
817 } else {
818 notificationManager.cancel(notificationId);
819 }
820 }
San Mehat36972292010-01-06 11:06:32 -0800821
822 public String[] getSecureCacheList() throws IllegalStateException {
823 return mListener.listAsec();
824 }
825
826 public String createSecureCache(String id, int sizeMb, String fstype,
827 String key, int ownerUid) throws IllegalStateException {
828 return mListener.createAsec(id, sizeMb, fstype, key, ownerUid);
829 }
830
831 public void finalizeSecureCache(String id) throws IllegalStateException {
832 mListener.finalizeAsec(id);
833 }
834
835 public void destroySecureCache(String id) throws IllegalStateException {
836 mListener.destroyAsec(id);
837 }
838
839 public String mountSecureCache(String id, String key, int ownerUid) throws IllegalStateException {
840 return mListener.mountAsec(id, key, ownerUid);
841 }
842
843 public String getSecureCachePath(String id) throws IllegalStateException {
844 return mListener.getAsecPath(id);
845 }
846
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847}
848