blob: 81ebe031938a1ff27d1c3e14a0b5ab22ad327d32 [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 Mehat7ebf0172010-01-12 07:57:42 -0800218 Log.d(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);
San Mehat7ebf0172010-01-12 07:57:42 -0800262
263 int retries = 12;
264 while (!state.equals(Environment.MEDIA_UNMOUNTED) && (retries-- >=0)) {
265 try {
266 Thread.sleep(1000);
267 } catch (InterruptedException iex) {
268 Log.e(TAG, "Interrupted while waiting for media", iex);
269 break;
270 }
271 state = Environment.getExternalStorageState();
272 }
273 if (retries == 0) {
274 Log.e(TAG, "Timed out waiting for media to unmount");
Jean-Baptiste Querufa101532010-01-12 11:53:42 -0800275 }
San Mehat91c77612010-01-07 10:39:41 -0800276 } catch (Exception e) {
277 Log.e(TAG, "external storage unmount failed", e);
278 }
279 }
280 }
281
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 /**
283 * @return true if USB mass storage support is enabled.
284 */
San Mehat36972292010-01-06 11:06:32 -0800285 public boolean getMassStorageEnabled() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800286 return mUmsEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 }
288
289 /**
290 * Enables or disables USB mass storage support.
291 *
292 * @param enable true to enable USB mass storage support
293 */
San Mehat36972292010-01-06 11:06:32 -0800294 public void setMassStorageEnabled(boolean enable) throws IllegalStateException {
San Mehat1f6301e2010-01-07 22:40:27 -0800295 if (mContext.checkCallingOrSelfPermission(
296 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
297 != PackageManager.PERMISSION_GRANTED) {
298 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
299 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800300 try {
301 String vp = Environment.getExternalStorageDirectory().getPath();
302 String vs = getVolumeState(vp);
303
304 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
San Mehat1f6301e2010-01-07 22:40:27 -0800305 mListener.unmountVolume(vp);
306 updateUsbMassStorageNotification(true, false);
San Mehat7fd0fee2009-12-17 07:12:23 -0800307 }
308
309 mListener.setShareMethodEnabled(Environment
310 .getExternalStorageDirectory()
311 .getPath(),
312 "ums", enable);
313 mUmsEnabled = enable;
314 if (!enable) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800315 mountMedia(vp);
San Mehat1f6301e2010-01-07 22:40:27 -0800316 if (mPromptUms) {
317 updateUsbMassStorageNotification(false, false);
318 } else {
319 updateUsbMassStorageNotification(true, false);
320 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800321 }
San Mehat36972292010-01-06 11:06:32 -0800322 } catch (IllegalStateException rex) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800323 Log.e(TAG, "Failed to set ums enable {" + enable + "}");
324 return;
325 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 }
327
328 /**
329 * @return true if USB mass storage is connected.
330 */
San Mehat36972292010-01-06 11:06:32 -0800331 public boolean getMassStorageConnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800332 return mUmsConnected;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800334
335 /**
336 * @return state of the volume at the specified mount point
337 */
San Mehat36972292010-01-06 11:06:32 -0800338 public String getVolumeState(String mountPoint) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800339 /*
340 * XXX: Until we have multiple volume discovery, just hardwire
341 * this to /sdcard
342 */
343 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
344 Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
345 throw new IllegalArgumentException();
346 }
347
348 return mLegacyState;
349 }
350
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351
352 /**
353 * Attempt to mount external media
354 */
San Mehat36972292010-01-06 11:06:32 -0800355 public void mountMedia(String mountPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 if (mContext.checkCallingOrSelfPermission(
357 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
358 != PackageManager.PERMISSION_GRANTED) {
359 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
360 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800361 mListener.mountVolume(mountPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 }
363
364 /**
365 * Attempt to unmount external media to prepare for eject
366 */
San Mehat36972292010-01-06 11:06:32 -0800367 public void unmountMedia(String mountPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 if (mContext.checkCallingOrSelfPermission(
369 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
370 != PackageManager.PERMISSION_GRANTED) {
371 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
372 }
373
374 // Set a flag so that when we get the unmounted event, we know
375 // to display the notification
376 mShowSafeUnmountNotificationWhenUnmounted = true;
377
378 // tell mountd to unmount the media
San Mehat7fd0fee2009-12-17 07:12:23 -0800379 mListener.unmountVolume(mountPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 }
381
382 /**
383 * Attempt to format external media
384 */
San Mehat36972292010-01-06 11:06:32 -0800385 public void formatMedia(String formatPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 if (mContext.checkCallingOrSelfPermission(
387 android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS)
388 != PackageManager.PERMISSION_GRANTED) {
389 throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
390 }
391
San Mehat7fd0fee2009-12-17 07:12:23 -0800392 mListener.formatVolume(formatPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 }
394
395 /**
396 * Returns true if we're playing media notification sounds.
397 */
398 public boolean getPlayNotificationSounds() {
399 return mPlaySounds;
400 }
401
402 /**
403 * Set whether or not we're playing media notification sounds.
404 */
405 public void setPlayNotificationSounds(boolean enabled) {
406 if (mContext.checkCallingOrSelfPermission(
407 android.Manifest.permission.WRITE_SETTINGS)
408 != PackageManager.PERMISSION_GRANTED) {
409 throw new SecurityException("Requires WRITE_SETTINGS permission");
410 }
411 mPlaySounds = enabled;
412 SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0"));
413 }
414
San Mehat7fd0fee2009-12-17 07:12:23 -0800415 void updatePublicVolumeState(String mountPoint, String state) {
416 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
417 Log.w(TAG, "Multiple volumes not currently supported");
418 return;
419 }
420 Log.w(TAG, "State for {" + mountPoint + "} = {" + state + "}");
421 mLegacyState = state;
422 }
423
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700424 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 * Update the state of the USB mass storage notification
426 */
427 void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) {
428
429 try {
430
431 if (getMassStorageConnected() && !suppressIfConnected) {
432 Intent intent = new Intent();
433 intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
Mike Lockwood95174432009-08-26 09:44:09 -0700434 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
436 setUsbStorageNotification(
437 com.android.internal.R.string.usb_storage_notification_title,
438 com.android.internal.R.string.usb_storage_notification_message,
439 com.android.internal.R.drawable.stat_sys_data_usb,
440 sound, true, pi);
441 } else {
442 setUsbStorageNotification(0, 0, 0, false, false, null);
443 }
San Mehat36972292010-01-06 11:06:32 -0800444 } catch (IllegalStateException e) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 // Nothing to do
446 }
447 }
448
449 void handlePossibleExplicitUnmountBroadcast(String path) {
450 if (mMounted) {
451 mMounted = false;
452 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
453 Uri.parse("file://" + path));
454 mContext.sendBroadcast(intent);
455 }
456 }
457
San Mehat7fd0fee2009-12-17 07:12:23 -0800458 void onVoldConnected() {
459 new Thread() {
460 public void run() {
461 try {
462 if (!getVolumeState(Environment.getExternalStorageDirectory().getPath())
463 .equals(Environment.MEDIA_MOUNTED)) {
464 try {
465 mountMedia(Environment.getExternalStorageDirectory().getPath());
San Mehat7fd0fee2009-12-17 07:12:23 -0800466 } catch (Exception ex) {
467 Log.w(TAG, "Connection-mount failed");
468 }
469 } else {
470 Log.d(TAG, "Skipping connection-mount; already mounted");
471 }
San Mehat36972292010-01-06 11:06:32 -0800472 } catch (IllegalStateException rex) {
San Mehat1f6301e2010-01-07 22:40:27 -0800473 Log.e(TAG, "Exception while handling connection mount ", rex);
San Mehat7fd0fee2009-12-17 07:12:23 -0800474 }
475
476 try {
477 boolean avail = mListener.getShareAvailable("ums");
478 notifyShareAvailabilityChange("ums", avail);
479 } catch (Exception ex) {
480 Log.w(TAG, "Failed to get share availability");
481 }
482 }
483 }.start();
484 }
485
486 void notifyVolumeStateChange(String label, String mountPoint, int oldState,
San Mehat36972292010-01-06 11:06:32 -0800487 int newState) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800488 String vs = getVolumeState(mountPoint);
489
490 if (newState == VolumeState.Init) {
491 } else if (newState == VolumeState.NoMedia) {
492 // NoMedia is handled via Disk Remove events
493 } else if (newState == VolumeState.Idle) {
494 // Don't notify if we're in BAD_REMOVAL, NOFS, or UNMOUNTABLE
495 if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) &&
496 !vs.equals(Environment.MEDIA_NOFS) &&
497 !vs.equals(Environment.MEDIA_UNMOUNTABLE)) {
498 notifyMediaUnmounted(mountPoint);
499 }
500 } else if (newState == VolumeState.Pending) {
501 } else if (newState == VolumeState.Checking) {
502 notifyMediaChecking(mountPoint);
503 } else if (newState == VolumeState.Mounted) {
504 notifyMediaMounted(mountPoint, false);
505 } else if (newState == VolumeState.Unmounting) {
506 notifyMediaUnmounting(mountPoint);
507 } else if (newState == VolumeState.Formatting) {
508 } else if (newState == VolumeState.Shared) {
509 notifyMediaShared(mountPoint, false);
510 } else if (newState == VolumeState.SharedMnt) {
511 notifyMediaShared(mountPoint, true);
512 } else {
513 Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
514 }
515 }
516
517
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518 /**
519 * Broadcasts the USB mass storage connected event to all clients.
520 */
521 void notifyUmsConnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800522 mUmsConnected = true;
523
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 String storageState = Environment.getExternalStorageState();
525 if (!storageState.equals(Environment.MEDIA_REMOVED) &&
526 !storageState.equals(Environment.MEDIA_BAD_REMOVAL) &&
527 !storageState.equals(Environment.MEDIA_CHECKING)) {
528
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700529 if (mAutoStartUms) {
530 try {
531 setMassStorageEnabled(true);
San Mehat36972292010-01-06 11:06:32 -0800532 } catch (IllegalStateException e) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700533 }
San Mehat1f6301e2010-01-07 22:40:27 -0800534 } else if (mPromptUms) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700535 updateUsbMassStorageNotification(false, true);
536 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 }
538
539 Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
540 mContext.sendBroadcast(intent);
541 }
542
San Mehat1f6301e2010-01-07 22:40:27 -0800543 void notifyShareAvailabilityChange(String method, final boolean avail) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800544 if (!method.equals("ums")) {
545 Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
546 return;
547 }
San Mehat1f6301e2010-01-07 22:40:27 -0800548
549 /*
550 * Notification needs to run in a different thread as
551 * it may need to call back into vold
552 */
553 new Thread() {
554 public void run() {
555 try {
556 if (avail) {
557 notifyUmsConnected();
558 } else {
559 notifyUmsDisconnected();
560 }
561 } catch (Exception ex) {
562 Log.w(TAG, "Failed to mount media on insertion");
563 }
564 }
565 }.start();
San Mehat7fd0fee2009-12-17 07:12:23 -0800566 }
567
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 /**
569 * Broadcasts the USB mass storage disconnected event to all clients.
570 */
571 void notifyUmsDisconnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800572 mUmsConnected = false;
San Mehat1f6301e2010-01-07 22:40:27 -0800573 if (mUmsEnabled) {
574 try {
575 Log.w(TAG, "UMS disconnected while enabled!");
576 setMassStorageEnabled(false);
577 } catch (Exception ex) {
578 Log.e(TAG, "Error disabling UMS on unsafe UMS disconnect", ex);
579 }
580 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 updateUsbMassStorageNotification(false, false);
582 Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
583 mContext.sendBroadcast(intent);
584 }
585
San Mehat36972292010-01-06 11:06:32 -0800586 void notifyMediaInserted(final String path) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800587 new Thread() {
588 public void run() {
589 try {
San Mehat7fd0fee2009-12-17 07:12:23 -0800590 mountMedia(path);
591 } catch (Exception ex) {
San Mehat1f6301e2010-01-07 22:40:27 -0800592 Log.w(TAG, "Failed to mount media on insertion", ex);
San Mehat7fd0fee2009-12-17 07:12:23 -0800593 }
594 }
595 }.start();
596 }
597
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 /**
599 * Broadcasts the media removed event to all clients.
600 */
San Mehat36972292010-01-06 11:06:32 -0800601 void notifyMediaRemoved(String path) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800602
603 // Suppress this on bad removal
604 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
605 return;
606 }
607
608 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
609
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 updateUsbMassStorageNotification(true, false);
611
612 setMediaStorageNotification(
San Mehat7fd0fee2009-12-17 07:12:23 -0800613 com.android.internal.R.string.ext_media_nomedia_notification_title,
614 com.android.internal.R.string.ext_media_nomedia_notification_message,
615 com.android.internal.R.drawable.stat_notify_sdcard_usb,
616 true, false, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 handlePossibleExplicitUnmountBroadcast(path);
618
619 Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED,
620 Uri.parse("file://" + path));
621 mContext.sendBroadcast(intent);
622 }
623
624 /**
625 * Broadcasts the media unmounted event to all clients.
626 */
627 void notifyMediaUnmounted(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800628
629 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
630
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 if (mShowSafeUnmountNotificationWhenUnmounted) {
632 setMediaStorageNotification(
633 com.android.internal.R.string.ext_media_safe_unmount_notification_title,
634 com.android.internal.R.string.ext_media_safe_unmount_notification_message,
Mike Lockwoodde46acdd2009-09-30 19:30:56 -0400635 com.android.internal.R.drawable.stat_notify_sdcard,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 true, true, null);
637 mShowSafeUnmountNotificationWhenUnmounted = false;
638 } else {
639 setMediaStorageNotification(0, 0, 0, false, false, null);
640 }
641 updateUsbMassStorageNotification(false, false);
642
643 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
644 Uri.parse("file://" + path));
645 mContext.sendBroadcast(intent);
646 }
647
648 /**
649 * Broadcasts the media checking event to all clients.
650 */
651 void notifyMediaChecking(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800652 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
653
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654 setMediaStorageNotification(
655 com.android.internal.R.string.ext_media_checking_notification_title,
656 com.android.internal.R.string.ext_media_checking_notification_message,
Mike Lockwoodde46acdd2009-09-30 19:30:56 -0400657 com.android.internal.R.drawable.stat_notify_sdcard_prepare,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 true, false, null);
659
660 updateUsbMassStorageNotification(true, false);
661 Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING,
662 Uri.parse("file://" + path));
663 mContext.sendBroadcast(intent);
664 }
665
666 /**
667 * Broadcasts the media nofs event to all clients.
668 */
669 void notifyMediaNoFs(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800670 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671
672 Intent intent = new Intent();
673 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
674 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
675
676 setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title,
677 com.android.internal.R.string.ext_media_nofs_notification_message,
Mike Lockwooda7ef2692009-09-10 11:15:26 -0400678 com.android.internal.R.drawable.stat_notify_sdcard_usb,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 true, false, pi);
680 updateUsbMassStorageNotification(false, false);
681 intent = new Intent(Intent.ACTION_MEDIA_NOFS,
682 Uri.parse("file://" + path));
683 mContext.sendBroadcast(intent);
684 }
685
686 /**
687 * Broadcasts the media mounted event to all clients.
688 */
689 void notifyMediaMounted(String path, boolean readOnly) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800690 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
691
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 setMediaStorageNotification(0, 0, 0, false, false, null);
693 updateUsbMassStorageNotification(false, false);
694 Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
695 Uri.parse("file://" + path));
696 intent.putExtra("read-only", readOnly);
697 mMounted = true;
698 mContext.sendBroadcast(intent);
699 }
700
701 /**
702 * Broadcasts the media shared event to all clients.
703 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800704 void notifyMediaShared(String path, boolean mounted) {
705 if (mounted) {
706 Log.e(TAG, "Live shared mounts not supported yet!");
707 return;
708 }
709
710 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
711
San Mehat1f6301e2010-01-07 22:40:27 -0800712 if (mUmsActiveNotify) {
713 Intent intent = new Intent();
714 intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
715 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
716 setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
717 com.android.internal.R.string.usb_storage_stop_notification_message,
718 com.android.internal.R.drawable.stat_sys_warning,
719 false, true, pi);
720 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800721 handlePossibleExplicitUnmountBroadcast(path);
San Mehat1f6301e2010-01-07 22:40:27 -0800722 Intent intent = new Intent(Intent.ACTION_MEDIA_SHARED,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800723 Uri.parse("file://" + path));
724 mContext.sendBroadcast(intent);
725 }
726
727 /**
728 * Broadcasts the media bad removal event to all clients.
729 */
730 void notifyMediaBadRemoval(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800731 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
732
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 updateUsbMassStorageNotification(true, false);
734 setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title,
735 com.android.internal.R.string.ext_media_badremoval_notification_message,
736 com.android.internal.R.drawable.stat_sys_warning,
737 true, true, null);
738
739 handlePossibleExplicitUnmountBroadcast(path);
740 Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL,
741 Uri.parse("file://" + path));
742 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743 }
744
745 /**
746 * Broadcasts the media unmountable event to all clients.
747 */
748 void notifyMediaUnmountable(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800749 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
750
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 Intent intent = new Intent();
752 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
753 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
754
755 setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title,
756 com.android.internal.R.string.ext_media_unmountable_notification_message,
Mike Lockwooda7ef2692009-09-10 11:15:26 -0400757 com.android.internal.R.drawable.stat_notify_sdcard_usb,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 true, false, pi);
759 updateUsbMassStorageNotification(false, false);
760
761 handlePossibleExplicitUnmountBroadcast(path);
762
763 intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE,
764 Uri.parse("file://" + path));
765 mContext.sendBroadcast(intent);
766 }
767
768 /**
769 * Broadcasts the media eject event to all clients.
770 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800771 void notifyMediaUnmounting(String path) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT,
773 Uri.parse("file://" + path));
774 mContext.sendBroadcast(intent);
775 }
776
777 /**
778 * Sets the USB storage notification.
779 */
780 private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible,
781 PendingIntent pi) {
782
783 if (!visible && mUsbStorageNotification == null) {
784 return;
785 }
786
787 NotificationManager notificationManager = (NotificationManager) mContext
788 .getSystemService(Context.NOTIFICATION_SERVICE);
789
790 if (notificationManager == null) {
791 return;
792 }
793
794 if (visible) {
795 Resources r = Resources.getSystem();
796 CharSequence title = r.getText(titleId);
797 CharSequence message = r.getText(messageId);
798
799 if (mUsbStorageNotification == null) {
800 mUsbStorageNotification = new Notification();
801 mUsbStorageNotification.icon = icon;
802 mUsbStorageNotification.when = 0;
803 }
804
805 if (sound && mPlaySounds) {
806 mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
807 } else {
808 mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
809 }
810
811 mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
812
813 mUsbStorageNotification.tickerText = title;
814 if (pi == null) {
815 Intent intent = new Intent();
816 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
817 }
818
819 mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
820 }
821
822 final int notificationId = mUsbStorageNotification.icon;
823 if (visible) {
824 notificationManager.notify(notificationId, mUsbStorageNotification);
825 } else {
826 notificationManager.cancel(notificationId);
827 }
828 }
829
830 private synchronized boolean getMediaStorageNotificationDismissable() {
831 if ((mMediaStorageNotification != null) &&
832 ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
833 Notification.FLAG_AUTO_CANCEL))
834 return true;
835
836 return false;
837 }
838
839 /**
840 * Sets the media storage notification.
841 */
842 private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
843 boolean dismissable, PendingIntent pi) {
844
845 if (!visible && mMediaStorageNotification == null) {
846 return;
847 }
848
849 NotificationManager notificationManager = (NotificationManager) mContext
850 .getSystemService(Context.NOTIFICATION_SERVICE);
851
852 if (notificationManager == null) {
853 return;
854 }
855
856 if (mMediaStorageNotification != null && visible) {
857 /*
858 * Dismiss the previous notification - we're about to
859 * re-use it.
860 */
861 final int notificationId = mMediaStorageNotification.icon;
862 notificationManager.cancel(notificationId);
863 }
864
865 if (visible) {
866 Resources r = Resources.getSystem();
867 CharSequence title = r.getText(titleId);
868 CharSequence message = r.getText(messageId);
869
870 if (mMediaStorageNotification == null) {
871 mMediaStorageNotification = new Notification();
872 mMediaStorageNotification.when = 0;
873 }
874
875 if (mPlaySounds) {
876 mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND;
877 } else {
878 mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
879 }
880
881 if (dismissable) {
882 mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
883 } else {
884 mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
885 }
886
887 mMediaStorageNotification.tickerText = title;
888 if (pi == null) {
889 Intent intent = new Intent();
890 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
891 }
892
893 mMediaStorageNotification.icon = icon;
894 mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
895 }
896
897 final int notificationId = mMediaStorageNotification.icon;
898 if (visible) {
899 notificationManager.notify(notificationId, mMediaStorageNotification);
900 } else {
901 notificationManager.cancel(notificationId);
902 }
903 }
San Mehat36972292010-01-06 11:06:32 -0800904
San Mehat0f5525a2010-01-11 10:15:16 -0800905 public String[] getSecureContainerList() throws IllegalStateException {
San Mehat36972292010-01-06 11:06:32 -0800906 return mListener.listAsec();
907 }
908
San Mehat0f5525a2010-01-11 10:15:16 -0800909 public String createSecureContainer(String id, int sizeMb, String fstype,
San Mehat36972292010-01-06 11:06:32 -0800910 String key, int ownerUid) throws IllegalStateException {
911 return mListener.createAsec(id, sizeMb, fstype, key, ownerUid);
912 }
913
San Mehat0f5525a2010-01-11 10:15:16 -0800914 public void finalizeSecureContainer(String id) throws IllegalStateException {
San Mehat36972292010-01-06 11:06:32 -0800915 mListener.finalizeAsec(id);
916 }
917
San Mehat0f5525a2010-01-11 10:15:16 -0800918 public void destroySecureContainer(String id) throws IllegalStateException {
San Mehat36972292010-01-06 11:06:32 -0800919 mListener.destroyAsec(id);
920 }
921
San Mehat0f5525a2010-01-11 10:15:16 -0800922 public String mountSecureContainer(String id, String key, int ownerUid) throws IllegalStateException {
San Mehat36972292010-01-06 11:06:32 -0800923 return mListener.mountAsec(id, key, ownerUid);
924 }
925
San Mehat0f5525a2010-01-11 10:15:16 -0800926 public String getSecureContainerPath(String id) throws IllegalStateException {
San Mehat36972292010-01-06 11:06:32 -0800927 return mListener.getAsecPath(id);
928 }
929
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930}
931