MountService.java revision 1f6301e1ff1a8ba04bc2b9c55fe6ceb883ce43bf
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server; 18 19import android.app.Notification; 20import android.app.NotificationManager; 21import android.app.PendingIntent; 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.content.pm.PackageManager; 27import android.content.res.Resources; 28import android.net.Uri; 29import android.os.IMountService; 30import android.os.Environment; 31import android.os.SystemProperties; 32import android.os.UEventObserver; 33import android.os.Handler; 34import android.text.TextUtils; 35import android.util.Log; 36 37import android.provider.Settings; 38import android.content.ContentResolver; 39import android.database.ContentObserver; 40 41import java.io.File; 42import java.io.FileReader; 43import java.lang.IllegalStateException; 44 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 53 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 66 /** 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 103 private SettingsWatcher mSettingsWatcher; 104 private boolean mAutoStartUms; 105 private boolean mPromptUms; 106 private boolean mUmsActiveNotify; 107 108 private boolean mUmsConnected = false; 109 private boolean mUmsEnabled = false; 110 111 private String mLegacyState = Environment.MEDIA_REMOVED; 112 113 /** 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"); 131 132 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 } 202 } 203 204 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 205 public void onReceive(Context context, Intent intent) { 206 String action = intent.getAction(); 207 208 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { 209 Thread thread = new Thread(mListener, MountListener.class.getName()); 210 thread.start(); 211 } 212 } 213 }; 214 215 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 272 /** 273 * @return true if USB mass storage support is enabled. 274 */ 275 public boolean getMassStorageEnabled() { 276 return mUmsEnabled; 277 } 278 279 /** 280 * Enables or disables USB mass storage support. 281 * 282 * @param enable true to enable USB mass storage support 283 */ 284 public void setMassStorageEnabled(boolean enable) throws IllegalStateException { 285 if (mContext.checkCallingOrSelfPermission( 286 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 287 != PackageManager.PERMISSION_GRANTED) { 288 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); 289 } 290 try { 291 String vp = Environment.getExternalStorageDirectory().getPath(); 292 String vs = getVolumeState(vp); 293 294 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) { 295 mListener.unmountVolume(vp); 296 updateUsbMassStorageNotification(true, false); 297 } 298 299 mListener.setShareMethodEnabled(Environment 300 .getExternalStorageDirectory() 301 .getPath(), 302 "ums", enable); 303 mUmsEnabled = enable; 304 if (!enable) { 305 mountMedia(vp); 306 if (mPromptUms) { 307 updateUsbMassStorageNotification(false, false); 308 } else { 309 updateUsbMassStorageNotification(true, false); 310 } 311 } 312 } catch (IllegalStateException rex) { 313 Log.e(TAG, "Failed to set ums enable {" + enable + "}"); 314 return; 315 } 316 } 317 318 /** 319 * @return true if USB mass storage is connected. 320 */ 321 public boolean getMassStorageConnected() { 322 return mUmsConnected; 323 } 324 325 /** 326 * @return state of the volume at the specified mount point 327 */ 328 public String getVolumeState(String mountPoint) throws IllegalStateException { 329 /* 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 341 342 /** 343 * Attempt to mount external media 344 */ 345 public void mountMedia(String mountPath) throws IllegalStateException { 346 if (mContext.checkCallingOrSelfPermission( 347 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 348 != PackageManager.PERMISSION_GRANTED) { 349 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); 350 } 351 mListener.mountVolume(mountPath); 352 } 353 354 /** 355 * Attempt to unmount external media to prepare for eject 356 */ 357 public void unmountMedia(String mountPath) throws IllegalStateException { 358 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 369 mListener.unmountVolume(mountPath); 370 } 371 372 /** 373 * Attempt to format external media 374 */ 375 public void formatMedia(String formatPath) throws IllegalStateException { 376 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 382 mListener.formatVolume(formatPath); 383 } 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 405 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 414 /** 415 * 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); 424 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 425 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 } 434 } catch (IllegalStateException e) { 435 // 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 448 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 } 463 } catch (IllegalStateException rex) { 464 Log.e(TAG, "Exception while handling connection mount ", rex); 465 } 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, 478 int newState) throws IllegalStateException { 479 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 509 /** 510 * Broadcasts the USB mass storage connected event to all clients. 511 */ 512 void notifyUmsConnected() { 513 mUmsConnected = true; 514 515 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 520 if (mAutoStartUms) { 521 try { 522 setMassStorageEnabled(true); 523 } catch (IllegalStateException e) { 524 } 525 } else if (mPromptUms) { 526 updateUsbMassStorageNotification(false, true); 527 } 528 } 529 530 Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED); 531 mContext.sendBroadcast(intent); 532 } 533 534 void notifyShareAvailabilityChange(String method, final boolean avail) { 535 if (!method.equals("ums")) { 536 Log.w(TAG, "Ignoring unsupported share method {" + method + "}"); 537 return; 538 } 539 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(); 557 } 558 559 /** 560 * Broadcasts the USB mass storage disconnected event to all clients. 561 */ 562 void notifyUmsDisconnected() { 563 mUmsConnected = false; 564 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 } 572 updateUsbMassStorageNotification(false, false); 573 Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED); 574 mContext.sendBroadcast(intent); 575 } 576 577 void notifyMediaInserted(final String path) throws IllegalStateException { 578 new Thread() { 579 public void run() { 580 try { 581 Log.d(TAG, "Mounting media after insertion"); 582 mountMedia(path); 583 } catch (Exception ex) { 584 Log.w(TAG, "Failed to mount media on insertion", ex); 585 } 586 } 587 }.start(); 588 } 589 590 /** 591 * Broadcasts the media removed event to all clients. 592 */ 593 void notifyMediaRemoved(String path) throws IllegalStateException { 594 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 602 updateUsbMassStorageNotification(true, false); 603 604 setMediaStorageNotification( 605 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); 609 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) { 620 621 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); 622 623 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, 627 com.android.internal.R.drawable.stat_notify_sdcard, 628 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) { 644 updatePublicVolumeState(path, Environment.MEDIA_CHECKING); 645 646 setMediaStorageNotification( 647 com.android.internal.R.string.ext_media_checking_notification_title, 648 com.android.internal.R.string.ext_media_checking_notification_message, 649 com.android.internal.R.drawable.stat_notify_sdcard_prepare, 650 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) { 662 updatePublicVolumeState(path, Environment.MEDIA_NOFS); 663 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, 670 com.android.internal.R.drawable.stat_notify_sdcard_usb, 671 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) { 682 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED); 683 684 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 */ 696 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 704 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 } 713 handlePossibleExplicitUnmountBroadcast(path); 714 Intent intent = new Intent(Intent.ACTION_MEDIA_SHARED, 715 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) { 723 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL); 724 725 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); 735 } 736 737 /** 738 * Broadcasts the media unmountable event to all clients. 739 */ 740 void notifyMediaUnmountable(String path) { 741 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE); 742 743 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, 749 com.android.internal.R.drawable.stat_notify_sdcard_usb, 750 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 */ 763 void notifyMediaUnmounting(String path) { 764 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 } 896 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 922} 923 924