blob: 166a2f1ea23476d602fec002dc8fede5480c31b9 [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;
San Mehat1f6301e2010-01-07 22:40:27 -080033import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.text.TextUtils;
35import android.util.Log;
36
San Mehat1f6301e2010-01-07 22:40:27 -080037import android.provider.Settings;
38import android.content.ContentResolver;
39import android.database.ContentObserver;
40
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import java.io.File;
42import java.io.FileReader;
San Mehat36972292010-01-06 11:06:32 -080043import java.lang.IllegalStateException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044
45/**
46 * MountService implements an to the mount service daemon
47 * @hide
48 */
49class MountService extends IMountService.Stub {
50
51 private static final String TAG = "MountService";
52
San Mehat7fd0fee2009-12-17 07:12:23 -080053 class VolumeState {
54 public static final int Init = -1;
55 public static final int NoMedia = 0;
56 public static final int Idle = 1;
57 public static final int Pending = 2;
58 public static final int Checking = 3;
59 public static final int Mounted = 4;
60 public static final int Unmounting = 5;
61 public static final int Formatting = 6;
62 public static final int Shared = 7;
63 public static final int SharedMnt = 8;
64 }
65
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 /**
67 * Binder context for this service
68 */
69 private Context mContext;
70
71 /**
72 * listener object for communicating with the mount service daemon
73 */
74 private MountListener mListener;
75
76 /**
77 * The notification that is shown when a USB mass storage host
78 * is connected.
79 * <p>
80 * This is lazily created, so use {@link #setUsbStorageNotification()}.
81 */
82 private Notification mUsbStorageNotification;
83
84
85 /**
86 * The notification that is shown when the following media events occur:
87 * - Media is being checked
88 * - Media is blank (or unknown filesystem)
89 * - Media is corrupt
90 * - Media is safe to unmount
91 * - Media is missing
92 * <p>
93 * This is lazily created, so use {@link #setMediaStorageNotification()}.
94 */
95 private Notification mMediaStorageNotification;
96
97 private boolean mShowSafeUnmountNotificationWhenUnmounted;
98
99 private boolean mPlaySounds;
100
101 private boolean mMounted;
102
San Mehat1f6301e2010-01-07 22:40:27 -0800103 private SettingsWatcher mSettingsWatcher;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700104 private boolean mAutoStartUms;
San Mehat1f6301e2010-01-07 22:40:27 -0800105 private boolean mPromptUms;
106 private boolean mUmsActiveNotify;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700107
San Mehat7fd0fee2009-12-17 07:12:23 -0800108 private boolean mUmsConnected = false;
109 private boolean mUmsEnabled = false;
110
111 private String mLegacyState = Environment.MEDIA_REMOVED;
112
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 /**
114 * Constructs a new MountService instance
115 *
116 * @param context Binder context for this service
117 */
118 public MountService(Context context) {
119 mContext = context;
120
121 // Register a BOOT_COMPLETED handler so that we can start
122 // MountListener. We defer the startup so that we don't
123 // start processing events before we ought-to
124 mContext.registerReceiver(mBroadcastReceiver,
125 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
126
127 mListener = new MountListener(this);
128 mShowSafeUnmountNotificationWhenUnmounted = false;
129
130 mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700131
San Mehat1f6301e2010-01-07 22:40:27 -0800132 ContentResolver cr = mContext.getContentResolver();
133 mAutoStartUms = (Settings.Secure.getInt(
134 cr, Settings.Secure.MOUNT_UMS_AUTOSTART, 0) == 1);
135 mPromptUms = (Settings.Secure.getInt(
136 cr, Settings.Secure.MOUNT_UMS_PROMPT, 1) == 1);
137 mUmsActiveNotify = (Settings.Secure.getInt(
138 cr, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, 1) == 1);
139
140 mSettingsWatcher = new SettingsWatcher(new Handler());
141 }
142
143 private class SettingsWatcher extends ContentObserver {
144 public SettingsWatcher(Handler handler) {
145 super(handler);
146 ContentResolver cr = mContext.getContentResolver();
147 cr.registerContentObserver(Settings.System.getUriFor(
148 Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND), false, this);
149 cr.registerContentObserver(Settings.Secure.getUriFor(
150 Settings.Secure.MOUNT_UMS_AUTOSTART), false, this);
151 cr.registerContentObserver(Settings.Secure.getUriFor(
152 Settings.Secure.MOUNT_UMS_PROMPT), false, this);
153 cr.registerContentObserver(Settings.Secure.getUriFor(
154 Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED), false, this);
155 }
156
157 public void onChange(boolean selfChange) {
158 super.onChange(selfChange);
159 ContentResolver cr = mContext.getContentResolver();
160
161 boolean newPlayNotificationSounds = (Settings.Secure.getInt(
162 cr, Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND, 1) == 1);
163
164 boolean newUmsAutostart = (Settings.Secure.getInt(
165 cr, Settings.Secure.MOUNT_UMS_AUTOSTART, 0) == 1);
166
167 if (newUmsAutostart != mAutoStartUms) {
San Mehat1f6301e2010-01-07 22:40:27 -0800168 mAutoStartUms = newUmsAutostart;
169 }
170
171 boolean newUmsPrompt = (Settings.Secure.getInt(
172 cr, Settings.Secure.MOUNT_UMS_PROMPT, 1) == 1);
173
174 if (newUmsPrompt != mPromptUms) {
San Mehat1f6301e2010-01-07 22:40:27 -0800175 mPromptUms = newUmsAutostart;
176 }
177
178 boolean newUmsNotifyEnabled = (Settings.Secure.getInt(
179 cr, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, 1) == 1);
180
San Mehat1f6301e2010-01-07 22:40:27 -0800181 if (mUmsEnabled) {
182 if (newUmsNotifyEnabled) {
183 Intent intent = new Intent();
184 intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
185 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
186 setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
187 com.android.internal.R.string.usb_storage_stop_notification_message,
188 com.android.internal.R.drawable.stat_sys_warning,
189 false, true, pi);
190 } else {
191 setUsbStorageNotification(0, 0, 0, false, false, null);
192 }
193 }
194 if (newUmsNotifyEnabled != mUmsActiveNotify) {
San Mehat1f6301e2010-01-07 22:40:27 -0800195 mUmsActiveNotify = newUmsNotifyEnabled;
196 }
197 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 }
199
200 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
201 public void onReceive(Context context, Intent intent) {
San Mehat91c77612010-01-07 10:39:41 -0800202 String action = intent.getAction();
203
204 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 Thread thread = new Thread(mListener, MountListener.class.getName());
206 thread.start();
207 }
208 }
209 };
210
San Mehat91c77612010-01-07 10:39:41 -0800211 public void shutdown() {
212 if (mContext.checkCallingOrSelfPermission(
213 android.Manifest.permission.SHUTDOWN)
214 != PackageManager.PERMISSION_GRANTED) {
215 throw new SecurityException("Requires SHUTDOWN permission");
216 }
217
San Mehatf9613582010-01-10 13:01:11 -0800218 Log.i(TAG, "Shutting down");
San Mehat91c77612010-01-07 10:39:41 -0800219 String state = Environment.getExternalStorageState();
220
221 if (state.equals(Environment.MEDIA_SHARED)) {
222 /*
223 * If the media is currently shared, unshare it.
224 * XXX: This is still dangerous!. We should not
225 * be rebooting at *all* if UMS is enabled, since
226 * the UMS host could have dirty FAT cache entries
227 * yet to flush.
228 */
229 try {
230 setMassStorageEnabled(false);
231 } catch (Exception e) {
232 Log.e(TAG, "ums disable failed", e);
233 }
234 } else if (state.equals(Environment.MEDIA_CHECKING)) {
235 /*
236 * If the media is being checked, then we need to wait for
237 * it to complete before being able to proceed.
238 */
239 // XXX: @hackbod - Should we disable the ANR timer here?
240 int retries = 30;
241 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
242 try {
243 Thread.sleep(1000);
244 } catch (InterruptedException iex) {
245 Log.e(TAG, "Interrupted while waiting for media", iex);
246 break;
247 }
248 state = Environment.getExternalStorageState();
249 }
250 if (retries == 0) {
251 Log.e(TAG, "Timed out waiting for media to check");
252 }
253 }
254
255 if (state.equals(Environment.MEDIA_MOUNTED)) {
256 /*
257 * If the media is mounted, then gracefully unmount it.
258 */
259 try {
260 String m = Environment.getExternalStorageDirectory().toString();
261 unmountMedia(m);
262 } catch (Exception e) {
263 Log.e(TAG, "external storage unmount failed", e);
264 }
265 }
266 }
267
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 /**
269 * @return true if USB mass storage support is enabled.
270 */
San Mehat36972292010-01-06 11:06:32 -0800271 public boolean getMassStorageEnabled() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800272 return mUmsEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 }
274
275 /**
276 * Enables or disables USB mass storage support.
277 *
278 * @param enable true to enable USB mass storage support
279 */
San Mehat36972292010-01-06 11:06:32 -0800280 public void setMassStorageEnabled(boolean enable) throws IllegalStateException {
San Mehat1f6301e2010-01-07 22:40:27 -0800281 if (mContext.checkCallingOrSelfPermission(
282 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
283 != PackageManager.PERMISSION_GRANTED) {
284 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
285 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800286 try {
287 String vp = Environment.getExternalStorageDirectory().getPath();
288 String vs = getVolumeState(vp);
289
290 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
San Mehat1f6301e2010-01-07 22:40:27 -0800291 mListener.unmountVolume(vp);
292 updateUsbMassStorageNotification(true, false);
San Mehat7fd0fee2009-12-17 07:12:23 -0800293 }
294
295 mListener.setShareMethodEnabled(Environment
296 .getExternalStorageDirectory()
297 .getPath(),
298 "ums", enable);
299 mUmsEnabled = enable;
300 if (!enable) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800301 mountMedia(vp);
San Mehat1f6301e2010-01-07 22:40:27 -0800302 if (mPromptUms) {
303 updateUsbMassStorageNotification(false, false);
304 } else {
305 updateUsbMassStorageNotification(true, false);
306 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800307 }
San Mehat36972292010-01-06 11:06:32 -0800308 } catch (IllegalStateException rex) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800309 Log.e(TAG, "Failed to set ums enable {" + enable + "}");
310 return;
311 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 }
313
314 /**
315 * @return true if USB mass storage is connected.
316 */
San Mehat36972292010-01-06 11:06:32 -0800317 public boolean getMassStorageConnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800318 return mUmsConnected;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800320
321 /**
322 * @return state of the volume at the specified mount point
323 */
San Mehat36972292010-01-06 11:06:32 -0800324 public String getVolumeState(String mountPoint) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800325 /*
326 * XXX: Until we have multiple volume discovery, just hardwire
327 * this to /sdcard
328 */
329 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
330 Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
331 throw new IllegalArgumentException();
332 }
333
334 return mLegacyState;
335 }
336
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337
338 /**
339 * Attempt to mount external media
340 */
San Mehat36972292010-01-06 11:06:32 -0800341 public void mountMedia(String mountPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 if (mContext.checkCallingOrSelfPermission(
343 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
344 != PackageManager.PERMISSION_GRANTED) {
345 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
346 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800347 mListener.mountVolume(mountPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 }
349
350 /**
351 * Attempt to unmount external media to prepare for eject
352 */
San Mehat36972292010-01-06 11:06:32 -0800353 public void unmountMedia(String mountPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 if (mContext.checkCallingOrSelfPermission(
355 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
356 != PackageManager.PERMISSION_GRANTED) {
357 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
358 }
359
360 // Set a flag so that when we get the unmounted event, we know
361 // to display the notification
362 mShowSafeUnmountNotificationWhenUnmounted = true;
363
364 // tell mountd to unmount the media
San Mehat7fd0fee2009-12-17 07:12:23 -0800365 mListener.unmountVolume(mountPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 }
367
368 /**
369 * Attempt to format external media
370 */
San Mehat36972292010-01-06 11:06:32 -0800371 public void formatMedia(String formatPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 if (mContext.checkCallingOrSelfPermission(
373 android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS)
374 != PackageManager.PERMISSION_GRANTED) {
375 throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
376 }
377
San Mehat7fd0fee2009-12-17 07:12:23 -0800378 mListener.formatVolume(formatPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 }
380
381 /**
382 * Returns true if we're playing media notification sounds.
383 */
384 public boolean getPlayNotificationSounds() {
385 return mPlaySounds;
386 }
387
388 /**
389 * Set whether or not we're playing media notification sounds.
390 */
391 public void setPlayNotificationSounds(boolean enabled) {
392 if (mContext.checkCallingOrSelfPermission(
393 android.Manifest.permission.WRITE_SETTINGS)
394 != PackageManager.PERMISSION_GRANTED) {
395 throw new SecurityException("Requires WRITE_SETTINGS permission");
396 }
397 mPlaySounds = enabled;
398 SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0"));
399 }
400
San Mehat7fd0fee2009-12-17 07:12:23 -0800401 void updatePublicVolumeState(String mountPoint, String state) {
402 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
403 Log.w(TAG, "Multiple volumes not currently supported");
404 return;
405 }
406 Log.w(TAG, "State for {" + mountPoint + "} = {" + state + "}");
407 mLegacyState = state;
408 }
409
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700410 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 * Update the state of the USB mass storage notification
412 */
413 void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) {
414
415 try {
416
417 if (getMassStorageConnected() && !suppressIfConnected) {
418 Intent intent = new Intent();
419 intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
Mike Lockwood95174432009-08-26 09:44:09 -0700420 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
422 setUsbStorageNotification(
423 com.android.internal.R.string.usb_storage_notification_title,
424 com.android.internal.R.string.usb_storage_notification_message,
425 com.android.internal.R.drawable.stat_sys_data_usb,
426 sound, true, pi);
427 } else {
428 setUsbStorageNotification(0, 0, 0, false, false, null);
429 }
San Mehat36972292010-01-06 11:06:32 -0800430 } catch (IllegalStateException e) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 // Nothing to do
432 }
433 }
434
435 void handlePossibleExplicitUnmountBroadcast(String path) {
436 if (mMounted) {
437 mMounted = false;
438 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
439 Uri.parse("file://" + path));
440 mContext.sendBroadcast(intent);
441 }
442 }
443
San Mehat7fd0fee2009-12-17 07:12:23 -0800444 void onVoldConnected() {
445 new Thread() {
446 public void run() {
447 try {
448 if (!getVolumeState(Environment.getExternalStorageDirectory().getPath())
449 .equals(Environment.MEDIA_MOUNTED)) {
450 try {
451 mountMedia(Environment.getExternalStorageDirectory().getPath());
San Mehat7fd0fee2009-12-17 07:12:23 -0800452 } catch (Exception ex) {
453 Log.w(TAG, "Connection-mount failed");
454 }
455 } else {
456 Log.d(TAG, "Skipping connection-mount; already mounted");
457 }
San Mehat36972292010-01-06 11:06:32 -0800458 } catch (IllegalStateException rex) {
San Mehat1f6301e2010-01-07 22:40:27 -0800459 Log.e(TAG, "Exception while handling connection mount ", rex);
San Mehat7fd0fee2009-12-17 07:12:23 -0800460 }
461
462 try {
463 boolean avail = mListener.getShareAvailable("ums");
464 notifyShareAvailabilityChange("ums", avail);
465 } catch (Exception ex) {
466 Log.w(TAG, "Failed to get share availability");
467 }
468 }
469 }.start();
470 }
471
472 void notifyVolumeStateChange(String label, String mountPoint, int oldState,
San Mehat36972292010-01-06 11:06:32 -0800473 int newState) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800474 String vs = getVolumeState(mountPoint);
475
476 if (newState == VolumeState.Init) {
477 } else if (newState == VolumeState.NoMedia) {
478 // NoMedia is handled via Disk Remove events
479 } else if (newState == VolumeState.Idle) {
480 // Don't notify if we're in BAD_REMOVAL, NOFS, or UNMOUNTABLE
481 if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) &&
482 !vs.equals(Environment.MEDIA_NOFS) &&
483 !vs.equals(Environment.MEDIA_UNMOUNTABLE)) {
484 notifyMediaUnmounted(mountPoint);
485 }
486 } else if (newState == VolumeState.Pending) {
487 } else if (newState == VolumeState.Checking) {
488 notifyMediaChecking(mountPoint);
489 } else if (newState == VolumeState.Mounted) {
490 notifyMediaMounted(mountPoint, false);
491 } else if (newState == VolumeState.Unmounting) {
492 notifyMediaUnmounting(mountPoint);
493 } else if (newState == VolumeState.Formatting) {
494 } else if (newState == VolumeState.Shared) {
495 notifyMediaShared(mountPoint, false);
496 } else if (newState == VolumeState.SharedMnt) {
497 notifyMediaShared(mountPoint, true);
498 } else {
499 Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
500 }
501 }
502
503
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 /**
505 * Broadcasts the USB mass storage connected event to all clients.
506 */
507 void notifyUmsConnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800508 mUmsConnected = true;
509
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 String storageState = Environment.getExternalStorageState();
511 if (!storageState.equals(Environment.MEDIA_REMOVED) &&
512 !storageState.equals(Environment.MEDIA_BAD_REMOVAL) &&
513 !storageState.equals(Environment.MEDIA_CHECKING)) {
514
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700515 if (mAutoStartUms) {
516 try {
517 setMassStorageEnabled(true);
San Mehat36972292010-01-06 11:06:32 -0800518 } catch (IllegalStateException e) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700519 }
San Mehat1f6301e2010-01-07 22:40:27 -0800520 } else if (mPromptUms) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700521 updateUsbMassStorageNotification(false, true);
522 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 }
524
525 Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
526 mContext.sendBroadcast(intent);
527 }
528
San Mehat1f6301e2010-01-07 22:40:27 -0800529 void notifyShareAvailabilityChange(String method, final boolean avail) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800530 if (!method.equals("ums")) {
531 Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
532 return;
533 }
San Mehat1f6301e2010-01-07 22:40:27 -0800534
535 /*
536 * Notification needs to run in a different thread as
537 * it may need to call back into vold
538 */
539 new Thread() {
540 public void run() {
541 try {
542 if (avail) {
543 notifyUmsConnected();
544 } else {
545 notifyUmsDisconnected();
546 }
547 } catch (Exception ex) {
548 Log.w(TAG, "Failed to mount media on insertion");
549 }
550 }
551 }.start();
San Mehat7fd0fee2009-12-17 07:12:23 -0800552 }
553
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 /**
555 * Broadcasts the USB mass storage disconnected event to all clients.
556 */
557 void notifyUmsDisconnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800558 mUmsConnected = false;
San Mehat1f6301e2010-01-07 22:40:27 -0800559 if (mUmsEnabled) {
560 try {
561 Log.w(TAG, "UMS disconnected while enabled!");
562 setMassStorageEnabled(false);
563 } catch (Exception ex) {
564 Log.e(TAG, "Error disabling UMS on unsafe UMS disconnect", ex);
565 }
566 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 updateUsbMassStorageNotification(false, false);
568 Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
569 mContext.sendBroadcast(intent);
570 }
571
San Mehat36972292010-01-06 11:06:32 -0800572 void notifyMediaInserted(final String path) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800573 new Thread() {
574 public void run() {
575 try {
San Mehat7fd0fee2009-12-17 07:12:23 -0800576 mountMedia(path);
577 } catch (Exception ex) {
San Mehat1f6301e2010-01-07 22:40:27 -0800578 Log.w(TAG, "Failed to mount media on insertion", ex);
San Mehat7fd0fee2009-12-17 07:12:23 -0800579 }
580 }
581 }.start();
582 }
583
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 /**
585 * Broadcasts the media removed event to all clients.
586 */
San Mehat36972292010-01-06 11:06:32 -0800587 void notifyMediaRemoved(String path) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800588
589 // Suppress this on bad removal
590 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
591 return;
592 }
593
594 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
595
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596 updateUsbMassStorageNotification(true, false);
597
598 setMediaStorageNotification(
San Mehat7fd0fee2009-12-17 07:12:23 -0800599 com.android.internal.R.string.ext_media_nomedia_notification_title,
600 com.android.internal.R.string.ext_media_nomedia_notification_message,
601 com.android.internal.R.drawable.stat_notify_sdcard_usb,
602 true, false, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 handlePossibleExplicitUnmountBroadcast(path);
604
605 Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED,
606 Uri.parse("file://" + path));
607 mContext.sendBroadcast(intent);
608 }
609
610 /**
611 * Broadcasts the media unmounted event to all clients.
612 */
613 void notifyMediaUnmounted(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800614
615 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
616
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 if (mShowSafeUnmountNotificationWhenUnmounted) {
618 setMediaStorageNotification(
619 com.android.internal.R.string.ext_media_safe_unmount_notification_title,
620 com.android.internal.R.string.ext_media_safe_unmount_notification_message,
Mike Lockwoodde46acdd2009-09-30 19:30:56 -0400621 com.android.internal.R.drawable.stat_notify_sdcard,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 true, true, null);
623 mShowSafeUnmountNotificationWhenUnmounted = false;
624 } else {
625 setMediaStorageNotification(0, 0, 0, false, false, null);
626 }
627 updateUsbMassStorageNotification(false, false);
628
629 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
630 Uri.parse("file://" + path));
631 mContext.sendBroadcast(intent);
632 }
633
634 /**
635 * Broadcasts the media checking event to all clients.
636 */
637 void notifyMediaChecking(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800638 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
639
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640 setMediaStorageNotification(
641 com.android.internal.R.string.ext_media_checking_notification_title,
642 com.android.internal.R.string.ext_media_checking_notification_message,
Mike Lockwoodde46acdd2009-09-30 19:30:56 -0400643 com.android.internal.R.drawable.stat_notify_sdcard_prepare,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 true, false, null);
645
646 updateUsbMassStorageNotification(true, false);
647 Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING,
648 Uri.parse("file://" + path));
649 mContext.sendBroadcast(intent);
650 }
651
652 /**
653 * Broadcasts the media nofs event to all clients.
654 */
655 void notifyMediaNoFs(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800656 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800657
658 Intent intent = new Intent();
659 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
660 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
661
662 setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title,
663 com.android.internal.R.string.ext_media_nofs_notification_message,
Mike Lockwooda7ef2692009-09-10 11:15:26 -0400664 com.android.internal.R.drawable.stat_notify_sdcard_usb,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 true, false, pi);
666 updateUsbMassStorageNotification(false, false);
667 intent = new Intent(Intent.ACTION_MEDIA_NOFS,
668 Uri.parse("file://" + path));
669 mContext.sendBroadcast(intent);
670 }
671
672 /**
673 * Broadcasts the media mounted event to all clients.
674 */
675 void notifyMediaMounted(String path, boolean readOnly) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800676 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
677
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678 setMediaStorageNotification(0, 0, 0, false, false, null);
679 updateUsbMassStorageNotification(false, false);
680 Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
681 Uri.parse("file://" + path));
682 intent.putExtra("read-only", readOnly);
683 mMounted = true;
684 mContext.sendBroadcast(intent);
685 }
686
687 /**
688 * Broadcasts the media shared event to all clients.
689 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800690 void notifyMediaShared(String path, boolean mounted) {
691 if (mounted) {
692 Log.e(TAG, "Live shared mounts not supported yet!");
693 return;
694 }
695
696 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
697
San Mehat1f6301e2010-01-07 22:40:27 -0800698 if (mUmsActiveNotify) {
699 Intent intent = new Intent();
700 intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
701 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
702 setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
703 com.android.internal.R.string.usb_storage_stop_notification_message,
704 com.android.internal.R.drawable.stat_sys_warning,
705 false, true, pi);
706 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 handlePossibleExplicitUnmountBroadcast(path);
San Mehat1f6301e2010-01-07 22:40:27 -0800708 Intent intent = new Intent(Intent.ACTION_MEDIA_SHARED,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709 Uri.parse("file://" + path));
710 mContext.sendBroadcast(intent);
711 }
712
713 /**
714 * Broadcasts the media bad removal event to all clients.
715 */
716 void notifyMediaBadRemoval(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800717 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
718
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 updateUsbMassStorageNotification(true, false);
720 setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title,
721 com.android.internal.R.string.ext_media_badremoval_notification_message,
722 com.android.internal.R.drawable.stat_sys_warning,
723 true, true, null);
724
725 handlePossibleExplicitUnmountBroadcast(path);
726 Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL,
727 Uri.parse("file://" + path));
728 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 }
730
731 /**
732 * Broadcasts the media unmountable event to all clients.
733 */
734 void notifyMediaUnmountable(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800735 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
736
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737 Intent intent = new Intent();
738 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
739 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
740
741 setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title,
742 com.android.internal.R.string.ext_media_unmountable_notification_message,
Mike Lockwooda7ef2692009-09-10 11:15:26 -0400743 com.android.internal.R.drawable.stat_notify_sdcard_usb,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 true, false, pi);
745 updateUsbMassStorageNotification(false, false);
746
747 handlePossibleExplicitUnmountBroadcast(path);
748
749 intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE,
750 Uri.parse("file://" + path));
751 mContext.sendBroadcast(intent);
752 }
753
754 /**
755 * Broadcasts the media eject event to all clients.
756 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800757 void notifyMediaUnmounting(String path) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT,
759 Uri.parse("file://" + path));
760 mContext.sendBroadcast(intent);
761 }
762
763 /**
764 * Sets the USB storage notification.
765 */
766 private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible,
767 PendingIntent pi) {
768
769 if (!visible && mUsbStorageNotification == null) {
770 return;
771 }
772
773 NotificationManager notificationManager = (NotificationManager) mContext
774 .getSystemService(Context.NOTIFICATION_SERVICE);
775
776 if (notificationManager == null) {
777 return;
778 }
779
780 if (visible) {
781 Resources r = Resources.getSystem();
782 CharSequence title = r.getText(titleId);
783 CharSequence message = r.getText(messageId);
784
785 if (mUsbStorageNotification == null) {
786 mUsbStorageNotification = new Notification();
787 mUsbStorageNotification.icon = icon;
788 mUsbStorageNotification.when = 0;
789 }
790
791 if (sound && mPlaySounds) {
792 mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
793 } else {
794 mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
795 }
796
797 mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
798
799 mUsbStorageNotification.tickerText = title;
800 if (pi == null) {
801 Intent intent = new Intent();
802 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
803 }
804
805 mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
806 }
807
808 final int notificationId = mUsbStorageNotification.icon;
809 if (visible) {
810 notificationManager.notify(notificationId, mUsbStorageNotification);
811 } else {
812 notificationManager.cancel(notificationId);
813 }
814 }
815
816 private synchronized boolean getMediaStorageNotificationDismissable() {
817 if ((mMediaStorageNotification != null) &&
818 ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
819 Notification.FLAG_AUTO_CANCEL))
820 return true;
821
822 return false;
823 }
824
825 /**
826 * Sets the media storage notification.
827 */
828 private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
829 boolean dismissable, PendingIntent pi) {
830
831 if (!visible && mMediaStorageNotification == null) {
832 return;
833 }
834
835 NotificationManager notificationManager = (NotificationManager) mContext
836 .getSystemService(Context.NOTIFICATION_SERVICE);
837
838 if (notificationManager == null) {
839 return;
840 }
841
842 if (mMediaStorageNotification != null && visible) {
843 /*
844 * Dismiss the previous notification - we're about to
845 * re-use it.
846 */
847 final int notificationId = mMediaStorageNotification.icon;
848 notificationManager.cancel(notificationId);
849 }
850
851 if (visible) {
852 Resources r = Resources.getSystem();
853 CharSequence title = r.getText(titleId);
854 CharSequence message = r.getText(messageId);
855
856 if (mMediaStorageNotification == null) {
857 mMediaStorageNotification = new Notification();
858 mMediaStorageNotification.when = 0;
859 }
860
861 if (mPlaySounds) {
862 mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND;
863 } else {
864 mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
865 }
866
867 if (dismissable) {
868 mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
869 } else {
870 mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
871 }
872
873 mMediaStorageNotification.tickerText = title;
874 if (pi == null) {
875 Intent intent = new Intent();
876 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
877 }
878
879 mMediaStorageNotification.icon = icon;
880 mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
881 }
882
883 final int notificationId = mMediaStorageNotification.icon;
884 if (visible) {
885 notificationManager.notify(notificationId, mMediaStorageNotification);
886 } else {
887 notificationManager.cancel(notificationId);
888 }
889 }
San Mehat36972292010-01-06 11:06:32 -0800890
891 public String[] getSecureCacheList() throws IllegalStateException {
892 return mListener.listAsec();
893 }
894
895 public String createSecureCache(String id, int sizeMb, String fstype,
896 String key, int ownerUid) throws IllegalStateException {
897 return mListener.createAsec(id, sizeMb, fstype, key, ownerUid);
898 }
899
900 public void finalizeSecureCache(String id) throws IllegalStateException {
901 mListener.finalizeAsec(id);
902 }
903
904 public void destroySecureCache(String id) throws IllegalStateException {
905 mListener.destroyAsec(id);
906 }
907
908 public String mountSecureCache(String id, String key, int ownerUid) throws IllegalStateException {
909 return mListener.mountAsec(id, key, ownerUid);
910 }
911
912 public String getSecureCachePath(String id) throws IllegalStateException {
913 return mListener.getAsecPath(id);
914 }
915
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916}
917