blob: bcdf5e8dad185aca047f14606fdbe73a733c2731 [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) {
168 Log.d(TAG, "Changing UMS autostart to " + newUmsAutostart);
169 mAutoStartUms = newUmsAutostart;
170 }
171
172 boolean newUmsPrompt = (Settings.Secure.getInt(
173 cr, Settings.Secure.MOUNT_UMS_PROMPT, 1) == 1);
174
175 if (newUmsPrompt != mPromptUms) {
176 Log.d(TAG, "Changing UMS prompt to " + newUmsPrompt);
177 mPromptUms = newUmsAutostart;
178 }
179
180 boolean newUmsNotifyEnabled = (Settings.Secure.getInt(
181 cr, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, 1) == 1);
182
183 Log.d(TAG, "new notify enabled = " + newUmsNotifyEnabled);
184 if (mUmsEnabled) {
185 if (newUmsNotifyEnabled) {
186 Intent intent = new Intent();
187 intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
188 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
189 setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
190 com.android.internal.R.string.usb_storage_stop_notification_message,
191 com.android.internal.R.drawable.stat_sys_warning,
192 false, true, pi);
193 } else {
194 setUsbStorageNotification(0, 0, 0, false, false, null);
195 }
196 }
197 if (newUmsNotifyEnabled != mUmsActiveNotify) {
198 Log.d(TAG, "Changing UMS active notification to " + newUmsNotifyEnabled);
199 mUmsActiveNotify = newUmsNotifyEnabled;
200 }
201 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 }
203
204 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
205 public void onReceive(Context context, Intent intent) {
San Mehat91c77612010-01-07 10:39:41 -0800206 String action = intent.getAction();
207
208 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 Thread thread = new Thread(mListener, MountListener.class.getName());
210 thread.start();
211 }
212 }
213 };
214
San Mehat91c77612010-01-07 10:39:41 -0800215 public void shutdown() {
216 if (mContext.checkCallingOrSelfPermission(
217 android.Manifest.permission.SHUTDOWN)
218 != PackageManager.PERMISSION_GRANTED) {
219 throw new SecurityException("Requires SHUTDOWN permission");
220 }
221
222 Log.d(TAG, "Shutting down");
223 String state = Environment.getExternalStorageState();
224
225 if (state.equals(Environment.MEDIA_SHARED)) {
226 /*
227 * If the media is currently shared, unshare it.
228 * XXX: This is still dangerous!. We should not
229 * be rebooting at *all* if UMS is enabled, since
230 * the UMS host could have dirty FAT cache entries
231 * yet to flush.
232 */
233 try {
234 setMassStorageEnabled(false);
235 } catch (Exception e) {
236 Log.e(TAG, "ums disable failed", e);
237 }
238 } else if (state.equals(Environment.MEDIA_CHECKING)) {
239 /*
240 * If the media is being checked, then we need to wait for
241 * it to complete before being able to proceed.
242 */
243 // XXX: @hackbod - Should we disable the ANR timer here?
244 int retries = 30;
245 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
246 try {
247 Thread.sleep(1000);
248 } catch (InterruptedException iex) {
249 Log.e(TAG, "Interrupted while waiting for media", iex);
250 break;
251 }
252 state = Environment.getExternalStorageState();
253 }
254 if (retries == 0) {
255 Log.e(TAG, "Timed out waiting for media to check");
256 }
257 }
258
259 if (state.equals(Environment.MEDIA_MOUNTED)) {
260 /*
261 * If the media is mounted, then gracefully unmount it.
262 */
263 try {
264 String m = Environment.getExternalStorageDirectory().toString();
265 unmountMedia(m);
266 } catch (Exception e) {
267 Log.e(TAG, "external storage unmount failed", e);
268 }
269 }
270 }
271
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 /**
273 * @return true if USB mass storage support is enabled.
274 */
San Mehat36972292010-01-06 11:06:32 -0800275 public boolean getMassStorageEnabled() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800276 return mUmsEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 }
278
279 /**
280 * Enables or disables USB mass storage support.
281 *
282 * @param enable true to enable USB mass storage support
283 */
San Mehat36972292010-01-06 11:06:32 -0800284 public void setMassStorageEnabled(boolean enable) throws IllegalStateException {
San Mehat1f6301e2010-01-07 22:40:27 -0800285 if (mContext.checkCallingOrSelfPermission(
286 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
287 != PackageManager.PERMISSION_GRANTED) {
288 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
289 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800290 try {
291 String vp = Environment.getExternalStorageDirectory().getPath();
292 String vs = getVolumeState(vp);
293
294 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
San Mehat1f6301e2010-01-07 22:40:27 -0800295 mListener.unmountVolume(vp);
296 updateUsbMassStorageNotification(true, false);
San Mehat7fd0fee2009-12-17 07:12:23 -0800297 }
298
299 mListener.setShareMethodEnabled(Environment
300 .getExternalStorageDirectory()
301 .getPath(),
302 "ums", enable);
303 mUmsEnabled = enable;
304 if (!enable) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800305 mountMedia(vp);
San Mehat1f6301e2010-01-07 22:40:27 -0800306 if (mPromptUms) {
307 updateUsbMassStorageNotification(false, false);
308 } else {
309 updateUsbMassStorageNotification(true, false);
310 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800311 }
San Mehat36972292010-01-06 11:06:32 -0800312 } catch (IllegalStateException rex) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800313 Log.e(TAG, "Failed to set ums enable {" + enable + "}");
314 return;
315 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 }
317
318 /**
319 * @return true if USB mass storage is connected.
320 */
San Mehat36972292010-01-06 11:06:32 -0800321 public boolean getMassStorageConnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800322 return mUmsConnected;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800324
325 /**
326 * @return state of the volume at the specified mount point
327 */
San Mehat36972292010-01-06 11:06:32 -0800328 public String getVolumeState(String mountPoint) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800329 /*
330 * XXX: Until we have multiple volume discovery, just hardwire
331 * this to /sdcard
332 */
333 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
334 Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
335 throw new IllegalArgumentException();
336 }
337
338 return mLegacyState;
339 }
340
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341
342 /**
343 * Attempt to mount external media
344 */
San Mehat36972292010-01-06 11:06:32 -0800345 public void mountMedia(String mountPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 if (mContext.checkCallingOrSelfPermission(
347 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
348 != PackageManager.PERMISSION_GRANTED) {
349 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
350 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800351 mListener.mountVolume(mountPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 }
353
354 /**
355 * Attempt to unmount external media to prepare for eject
356 */
San Mehat36972292010-01-06 11:06:32 -0800357 public void unmountMedia(String mountPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 if (mContext.checkCallingOrSelfPermission(
359 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
360 != PackageManager.PERMISSION_GRANTED) {
361 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
362 }
363
364 // Set a flag so that when we get the unmounted event, we know
365 // to display the notification
366 mShowSafeUnmountNotificationWhenUnmounted = true;
367
368 // tell mountd to unmount the media
San Mehat7fd0fee2009-12-17 07:12:23 -0800369 mListener.unmountVolume(mountPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 }
371
372 /**
373 * Attempt to format external media
374 */
San Mehat36972292010-01-06 11:06:32 -0800375 public void formatMedia(String formatPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 if (mContext.checkCallingOrSelfPermission(
377 android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS)
378 != PackageManager.PERMISSION_GRANTED) {
379 throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
380 }
381
San Mehat7fd0fee2009-12-17 07:12:23 -0800382 mListener.formatVolume(formatPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 }
384
385 /**
386 * Returns true if we're playing media notification sounds.
387 */
388 public boolean getPlayNotificationSounds() {
389 return mPlaySounds;
390 }
391
392 /**
393 * Set whether or not we're playing media notification sounds.
394 */
395 public void setPlayNotificationSounds(boolean enabled) {
396 if (mContext.checkCallingOrSelfPermission(
397 android.Manifest.permission.WRITE_SETTINGS)
398 != PackageManager.PERMISSION_GRANTED) {
399 throw new SecurityException("Requires WRITE_SETTINGS permission");
400 }
401 mPlaySounds = enabled;
402 SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0"));
403 }
404
San Mehat7fd0fee2009-12-17 07:12:23 -0800405 void updatePublicVolumeState(String mountPoint, String state) {
406 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
407 Log.w(TAG, "Multiple volumes not currently supported");
408 return;
409 }
410 Log.w(TAG, "State for {" + mountPoint + "} = {" + state + "}");
411 mLegacyState = state;
412 }
413
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700414 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 * Update the state of the USB mass storage notification
416 */
417 void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) {
418
419 try {
420
421 if (getMassStorageConnected() && !suppressIfConnected) {
422 Intent intent = new Intent();
423 intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
Mike Lockwood95174432009-08-26 09:44:09 -0700424 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
426 setUsbStorageNotification(
427 com.android.internal.R.string.usb_storage_notification_title,
428 com.android.internal.R.string.usb_storage_notification_message,
429 com.android.internal.R.drawable.stat_sys_data_usb,
430 sound, true, pi);
431 } else {
432 setUsbStorageNotification(0, 0, 0, false, false, null);
433 }
San Mehat36972292010-01-06 11:06:32 -0800434 } catch (IllegalStateException e) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 // Nothing to do
436 }
437 }
438
439 void handlePossibleExplicitUnmountBroadcast(String path) {
440 if (mMounted) {
441 mMounted = false;
442 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
443 Uri.parse("file://" + path));
444 mContext.sendBroadcast(intent);
445 }
446 }
447
San Mehat7fd0fee2009-12-17 07:12:23 -0800448 void onVoldConnected() {
449 new Thread() {
450 public void run() {
451 try {
452 if (!getVolumeState(Environment.getExternalStorageDirectory().getPath())
453 .equals(Environment.MEDIA_MOUNTED)) {
454 try {
455 mountMedia(Environment.getExternalStorageDirectory().getPath());
456 Log.d(TAG, "Connection-mount suceeded");
457 } catch (Exception ex) {
458 Log.w(TAG, "Connection-mount failed");
459 }
460 } else {
461 Log.d(TAG, "Skipping connection-mount; already mounted");
462 }
San Mehat36972292010-01-06 11:06:32 -0800463 } catch (IllegalStateException rex) {
San Mehat1f6301e2010-01-07 22:40:27 -0800464 Log.e(TAG, "Exception while handling connection mount ", rex);
San Mehat7fd0fee2009-12-17 07:12:23 -0800465 }
466
467 try {
468 boolean avail = mListener.getShareAvailable("ums");
469 notifyShareAvailabilityChange("ums", avail);
470 } catch (Exception ex) {
471 Log.w(TAG, "Failed to get share availability");
472 }
473 }
474 }.start();
475 }
476
477 void notifyVolumeStateChange(String label, String mountPoint, int oldState,
San Mehat36972292010-01-06 11:06:32 -0800478 int newState) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800479 String vs = getVolumeState(mountPoint);
480
481 if (newState == VolumeState.Init) {
482 } else if (newState == VolumeState.NoMedia) {
483 // NoMedia is handled via Disk Remove events
484 } else if (newState == VolumeState.Idle) {
485 // Don't notify if we're in BAD_REMOVAL, NOFS, or UNMOUNTABLE
486 if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) &&
487 !vs.equals(Environment.MEDIA_NOFS) &&
488 !vs.equals(Environment.MEDIA_UNMOUNTABLE)) {
489 notifyMediaUnmounted(mountPoint);
490 }
491 } else if (newState == VolumeState.Pending) {
492 } else if (newState == VolumeState.Checking) {
493 notifyMediaChecking(mountPoint);
494 } else if (newState == VolumeState.Mounted) {
495 notifyMediaMounted(mountPoint, false);
496 } else if (newState == VolumeState.Unmounting) {
497 notifyMediaUnmounting(mountPoint);
498 } else if (newState == VolumeState.Formatting) {
499 } else if (newState == VolumeState.Shared) {
500 notifyMediaShared(mountPoint, false);
501 } else if (newState == VolumeState.SharedMnt) {
502 notifyMediaShared(mountPoint, true);
503 } else {
504 Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
505 }
506 }
507
508
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 /**
510 * Broadcasts the USB mass storage connected event to all clients.
511 */
512 void notifyUmsConnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800513 mUmsConnected = true;
514
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 String storageState = Environment.getExternalStorageState();
516 if (!storageState.equals(Environment.MEDIA_REMOVED) &&
517 !storageState.equals(Environment.MEDIA_BAD_REMOVAL) &&
518 !storageState.equals(Environment.MEDIA_CHECKING)) {
519
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700520 if (mAutoStartUms) {
521 try {
522 setMassStorageEnabled(true);
San Mehat36972292010-01-06 11:06:32 -0800523 } catch (IllegalStateException e) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700524 }
San Mehat1f6301e2010-01-07 22:40:27 -0800525 } else if (mPromptUms) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700526 updateUsbMassStorageNotification(false, true);
527 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 }
529
530 Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
531 mContext.sendBroadcast(intent);
532 }
533
San Mehat1f6301e2010-01-07 22:40:27 -0800534 void notifyShareAvailabilityChange(String method, final boolean avail) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800535 if (!method.equals("ums")) {
536 Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
537 return;
538 }
San Mehat1f6301e2010-01-07 22:40:27 -0800539
540 /*
541 * Notification needs to run in a different thread as
542 * it may need to call back into vold
543 */
544 new Thread() {
545 public void run() {
546 try {
547 if (avail) {
548 notifyUmsConnected();
549 } else {
550 notifyUmsDisconnected();
551 }
552 } catch (Exception ex) {
553 Log.w(TAG, "Failed to mount media on insertion");
554 }
555 }
556 }.start();
San Mehat7fd0fee2009-12-17 07:12:23 -0800557 }
558
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 /**
560 * Broadcasts the USB mass storage disconnected event to all clients.
561 */
562 void notifyUmsDisconnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800563 mUmsConnected = false;
San Mehat1f6301e2010-01-07 22:40:27 -0800564 if (mUmsEnabled) {
565 try {
566 Log.w(TAG, "UMS disconnected while enabled!");
567 setMassStorageEnabled(false);
568 } catch (Exception ex) {
569 Log.e(TAG, "Error disabling UMS on unsafe UMS disconnect", ex);
570 }
571 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 updateUsbMassStorageNotification(false, false);
573 Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
574 mContext.sendBroadcast(intent);
575 }
576
San Mehat36972292010-01-06 11:06:32 -0800577 void notifyMediaInserted(final String path) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800578 new Thread() {
579 public void run() {
580 try {
581 Log.d(TAG, "Mounting media after insertion");
582 mountMedia(path);
583 } catch (Exception ex) {
San Mehat1f6301e2010-01-07 22:40:27 -0800584 Log.w(TAG, "Failed to mount media on insertion", ex);
San Mehat7fd0fee2009-12-17 07:12:23 -0800585 }
586 }
587 }.start();
588 }
589
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 /**
591 * Broadcasts the media removed event to all clients.
592 */
San Mehat36972292010-01-06 11:06:32 -0800593 void notifyMediaRemoved(String path) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800594
595 // Suppress this on bad removal
596 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
597 return;
598 }
599
600 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
601
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 updateUsbMassStorageNotification(true, false);
603
604 setMediaStorageNotification(
San Mehat7fd0fee2009-12-17 07:12:23 -0800605 com.android.internal.R.string.ext_media_nomedia_notification_title,
606 com.android.internal.R.string.ext_media_nomedia_notification_message,
607 com.android.internal.R.drawable.stat_notify_sdcard_usb,
608 true, false, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 handlePossibleExplicitUnmountBroadcast(path);
610
611 Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED,
612 Uri.parse("file://" + path));
613 mContext.sendBroadcast(intent);
614 }
615
616 /**
617 * Broadcasts the media unmounted event to all clients.
618 */
619 void notifyMediaUnmounted(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800620
621 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
622
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 if (mShowSafeUnmountNotificationWhenUnmounted) {
624 setMediaStorageNotification(
625 com.android.internal.R.string.ext_media_safe_unmount_notification_title,
626 com.android.internal.R.string.ext_media_safe_unmount_notification_message,
Mike Lockwoodde46acdd2009-09-30 19:30:56 -0400627 com.android.internal.R.drawable.stat_notify_sdcard,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 true, true, null);
629 mShowSafeUnmountNotificationWhenUnmounted = false;
630 } else {
631 setMediaStorageNotification(0, 0, 0, false, false, null);
632 }
633 updateUsbMassStorageNotification(false, false);
634
635 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
636 Uri.parse("file://" + path));
637 mContext.sendBroadcast(intent);
638 }
639
640 /**
641 * Broadcasts the media checking event to all clients.
642 */
643 void notifyMediaChecking(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800644 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
645
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 setMediaStorageNotification(
647 com.android.internal.R.string.ext_media_checking_notification_title,
648 com.android.internal.R.string.ext_media_checking_notification_message,
Mike Lockwoodde46acdd2009-09-30 19:30:56 -0400649 com.android.internal.R.drawable.stat_notify_sdcard_prepare,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800650 true, false, null);
651
652 updateUsbMassStorageNotification(true, false);
653 Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING,
654 Uri.parse("file://" + path));
655 mContext.sendBroadcast(intent);
656 }
657
658 /**
659 * Broadcasts the media nofs event to all clients.
660 */
661 void notifyMediaNoFs(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800662 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663
664 Intent intent = new Intent();
665 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
666 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
667
668 setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title,
669 com.android.internal.R.string.ext_media_nofs_notification_message,
Mike Lockwooda7ef2692009-09-10 11:15:26 -0400670 com.android.internal.R.drawable.stat_notify_sdcard_usb,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 true, false, pi);
672 updateUsbMassStorageNotification(false, false);
673 intent = new Intent(Intent.ACTION_MEDIA_NOFS,
674 Uri.parse("file://" + path));
675 mContext.sendBroadcast(intent);
676 }
677
678 /**
679 * Broadcasts the media mounted event to all clients.
680 */
681 void notifyMediaMounted(String path, boolean readOnly) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800682 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
683
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 setMediaStorageNotification(0, 0, 0, false, false, null);
685 updateUsbMassStorageNotification(false, false);
686 Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
687 Uri.parse("file://" + path));
688 intent.putExtra("read-only", readOnly);
689 mMounted = true;
690 mContext.sendBroadcast(intent);
691 }
692
693 /**
694 * Broadcasts the media shared event to all clients.
695 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800696 void notifyMediaShared(String path, boolean mounted) {
697 if (mounted) {
698 Log.e(TAG, "Live shared mounts not supported yet!");
699 return;
700 }
701
702 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
703
San Mehat1f6301e2010-01-07 22:40:27 -0800704 if (mUmsActiveNotify) {
705 Intent intent = new Intent();
706 intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
707 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
708 setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
709 com.android.internal.R.string.usb_storage_stop_notification_message,
710 com.android.internal.R.drawable.stat_sys_warning,
711 false, true, pi);
712 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800713 handlePossibleExplicitUnmountBroadcast(path);
San Mehat1f6301e2010-01-07 22:40:27 -0800714 Intent intent = new Intent(Intent.ACTION_MEDIA_SHARED,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 Uri.parse("file://" + path));
716 mContext.sendBroadcast(intent);
717 }
718
719 /**
720 * Broadcasts the media bad removal event to all clients.
721 */
722 void notifyMediaBadRemoval(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800723 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
724
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 updateUsbMassStorageNotification(true, false);
726 setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title,
727 com.android.internal.R.string.ext_media_badremoval_notification_message,
728 com.android.internal.R.drawable.stat_sys_warning,
729 true, true, null);
730
731 handlePossibleExplicitUnmountBroadcast(path);
732 Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL,
733 Uri.parse("file://" + path));
734 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 }
736
737 /**
738 * Broadcasts the media unmountable event to all clients.
739 */
740 void notifyMediaUnmountable(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800741 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
742
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743 Intent intent = new Intent();
744 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
745 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
746
747 setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title,
748 com.android.internal.R.string.ext_media_unmountable_notification_message,
Mike Lockwooda7ef2692009-09-10 11:15:26 -0400749 com.android.internal.R.drawable.stat_notify_sdcard_usb,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 true, false, pi);
751 updateUsbMassStorageNotification(false, false);
752
753 handlePossibleExplicitUnmountBroadcast(path);
754
755 intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE,
756 Uri.parse("file://" + path));
757 mContext.sendBroadcast(intent);
758 }
759
760 /**
761 * Broadcasts the media eject event to all clients.
762 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800763 void notifyMediaUnmounting(String path) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT,
765 Uri.parse("file://" + path));
766 mContext.sendBroadcast(intent);
767 }
768
769 /**
770 * Sets the USB storage notification.
771 */
772 private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible,
773 PendingIntent pi) {
774
775 if (!visible && mUsbStorageNotification == null) {
776 return;
777 }
778
779 NotificationManager notificationManager = (NotificationManager) mContext
780 .getSystemService(Context.NOTIFICATION_SERVICE);
781
782 if (notificationManager == null) {
783 return;
784 }
785
786 if (visible) {
787 Resources r = Resources.getSystem();
788 CharSequence title = r.getText(titleId);
789 CharSequence message = r.getText(messageId);
790
791 if (mUsbStorageNotification == null) {
792 mUsbStorageNotification = new Notification();
793 mUsbStorageNotification.icon = icon;
794 mUsbStorageNotification.when = 0;
795 }
796
797 if (sound && mPlaySounds) {
798 mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
799 } else {
800 mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
801 }
802
803 mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
804
805 mUsbStorageNotification.tickerText = title;
806 if (pi == null) {
807 Intent intent = new Intent();
808 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
809 }
810
811 mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
812 }
813
814 final int notificationId = mUsbStorageNotification.icon;
815 if (visible) {
816 notificationManager.notify(notificationId, mUsbStorageNotification);
817 } else {
818 notificationManager.cancel(notificationId);
819 }
820 }
821
822 private synchronized boolean getMediaStorageNotificationDismissable() {
823 if ((mMediaStorageNotification != null) &&
824 ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
825 Notification.FLAG_AUTO_CANCEL))
826 return true;
827
828 return false;
829 }
830
831 /**
832 * Sets the media storage notification.
833 */
834 private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
835 boolean dismissable, PendingIntent pi) {
836
837 if (!visible && mMediaStorageNotification == null) {
838 return;
839 }
840
841 NotificationManager notificationManager = (NotificationManager) mContext
842 .getSystemService(Context.NOTIFICATION_SERVICE);
843
844 if (notificationManager == null) {
845 return;
846 }
847
848 if (mMediaStorageNotification != null && visible) {
849 /*
850 * Dismiss the previous notification - we're about to
851 * re-use it.
852 */
853 final int notificationId = mMediaStorageNotification.icon;
854 notificationManager.cancel(notificationId);
855 }
856
857 if (visible) {
858 Resources r = Resources.getSystem();
859 CharSequence title = r.getText(titleId);
860 CharSequence message = r.getText(messageId);
861
862 if (mMediaStorageNotification == null) {
863 mMediaStorageNotification = new Notification();
864 mMediaStorageNotification.when = 0;
865 }
866
867 if (mPlaySounds) {
868 mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND;
869 } else {
870 mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
871 }
872
873 if (dismissable) {
874 mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
875 } else {
876 mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
877 }
878
879 mMediaStorageNotification.tickerText = title;
880 if (pi == null) {
881 Intent intent = new Intent();
882 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
883 }
884
885 mMediaStorageNotification.icon = icon;
886 mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
887 }
888
889 final int notificationId = mMediaStorageNotification.icon;
890 if (visible) {
891 notificationManager.notify(notificationId, mMediaStorageNotification);
892 } else {
893 notificationManager.cancel(notificationId);
894 }
895 }
San Mehat36972292010-01-06 11:06:32 -0800896
897 public String[] getSecureCacheList() throws IllegalStateException {
898 return mListener.listAsec();
899 }
900
901 public String createSecureCache(String id, int sizeMb, String fstype,
902 String key, int ownerUid) throws IllegalStateException {
903 return mListener.createAsec(id, sizeMb, fstype, key, ownerUid);
904 }
905
906 public void finalizeSecureCache(String id) throws IllegalStateException {
907 mListener.finalizeAsec(id);
908 }
909
910 public void destroySecureCache(String id) throws IllegalStateException {
911 mListener.destroyAsec(id);
912 }
913
914 public String mountSecureCache(String id, String key, int ownerUid) throws IllegalStateException {
915 return mListener.mountAsec(id, key, ownerUid);
916 }
917
918 public String getSecureCachePath(String id) throws IllegalStateException {
919 return mListener.getAsecPath(id);
920 }
921
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922}
923