MountService.java revision 0f5525ad3b9b341a6c288ded8a8a08572fc657c6
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 mAutoStartUms = newUmsAutostart; 169 } 170 171 boolean newUmsPrompt = (Settings.Secure.getInt( 172 cr, Settings.Secure.MOUNT_UMS_PROMPT, 1) == 1); 173 174 if (newUmsPrompt != mPromptUms) { 175 mPromptUms = newUmsAutostart; 176 } 177 178 boolean newUmsNotifyEnabled = (Settings.Secure.getInt( 179 cr, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, 1) == 1); 180 181 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) { 195 mUmsActiveNotify = newUmsNotifyEnabled; 196 } 197 } 198 } 199 200 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 201 public void onReceive(Context context, Intent intent) { 202 String action = intent.getAction(); 203 204 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { 205 Thread thread = new Thread(mListener, MountListener.class.getName()); 206 thread.start(); 207 } 208 } 209 }; 210 211 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 218 Log.i(TAG, "Shutting down"); 219 String state = Environment.getExternalStorageState(); 220 221 if (state.equals(Environment.MEDIA_SHARED)) { 222 /* 223 * If the media is currently shared, unshare it. 224 * XXX: This is still dangerous!. We should not 225 * be rebooting at *all* if UMS is enabled, since 226 * the UMS host could have dirty FAT cache entries 227 * yet to flush. 228 */ 229 try { 230 setMassStorageEnabled(false); 231 } catch (Exception e) { 232 Log.e(TAG, "ums disable failed", e); 233 } 234 } else if (state.equals(Environment.MEDIA_CHECKING)) { 235 /* 236 * If the media is being checked, then we need to wait for 237 * it to complete before being able to proceed. 238 */ 239 // XXX: @hackbod - Should we disable the ANR timer here? 240 int retries = 30; 241 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) { 242 try { 243 Thread.sleep(1000); 244 } catch (InterruptedException iex) { 245 Log.e(TAG, "Interrupted while waiting for media", iex); 246 break; 247 } 248 state = Environment.getExternalStorageState(); 249 } 250 if (retries == 0) { 251 Log.e(TAG, "Timed out waiting for media to check"); 252 } 253 } 254 255 if (state.equals(Environment.MEDIA_MOUNTED)) { 256 /* 257 * If the media is mounted, then gracefully unmount it. 258 */ 259 try { 260 String m = Environment.getExternalStorageDirectory().toString(); 261 unmountMedia(m); 262 } catch (Exception e) { 263 Log.e(TAG, "external storage unmount failed", e); 264 } 265 } 266 } 267 268 /** 269 * @return true if USB mass storage support is enabled. 270 */ 271 public boolean getMassStorageEnabled() { 272 return mUmsEnabled; 273 } 274 275 /** 276 * Enables or disables USB mass storage support. 277 * 278 * @param enable true to enable USB mass storage support 279 */ 280 public void setMassStorageEnabled(boolean enable) throws IllegalStateException { 281 if (mContext.checkCallingOrSelfPermission( 282 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 283 != PackageManager.PERMISSION_GRANTED) { 284 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); 285 } 286 try { 287 String vp = Environment.getExternalStorageDirectory().getPath(); 288 String vs = getVolumeState(vp); 289 290 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) { 291 mListener.unmountVolume(vp); 292 updateUsbMassStorageNotification(true, false); 293 } 294 295 mListener.setShareMethodEnabled(Environment 296 .getExternalStorageDirectory() 297 .getPath(), 298 "ums", enable); 299 mUmsEnabled = enable; 300 if (!enable) { 301 mountMedia(vp); 302 if (mPromptUms) { 303 updateUsbMassStorageNotification(false, false); 304 } else { 305 updateUsbMassStorageNotification(true, false); 306 } 307 } 308 } catch (IllegalStateException rex) { 309 Log.e(TAG, "Failed to set ums enable {" + enable + "}"); 310 return; 311 } 312 } 313 314 /** 315 * @return true if USB mass storage is connected. 316 */ 317 public boolean getMassStorageConnected() { 318 return mUmsConnected; 319 } 320 321 /** 322 * @return state of the volume at the specified mount point 323 */ 324 public String getVolumeState(String mountPoint) throws IllegalStateException { 325 /* 326 * XXX: Until we have multiple volume discovery, just hardwire 327 * this to /sdcard 328 */ 329 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) { 330 Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume"); 331 throw new IllegalArgumentException(); 332 } 333 334 return mLegacyState; 335 } 336 337 338 /** 339 * Attempt to mount external media 340 */ 341 public void mountMedia(String mountPath) throws IllegalStateException { 342 if (mContext.checkCallingOrSelfPermission( 343 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 344 != PackageManager.PERMISSION_GRANTED) { 345 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); 346 } 347 mListener.mountVolume(mountPath); 348 } 349 350 /** 351 * Attempt to unmount external media to prepare for eject 352 */ 353 public void unmountMedia(String mountPath) throws IllegalStateException { 354 if (mContext.checkCallingOrSelfPermission( 355 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 356 != PackageManager.PERMISSION_GRANTED) { 357 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); 358 } 359 360 // Set a flag so that when we get the unmounted event, we know 361 // to display the notification 362 mShowSafeUnmountNotificationWhenUnmounted = true; 363 364 // tell mountd to unmount the media 365 mListener.unmountVolume(mountPath); 366 } 367 368 /** 369 * Attempt to format external media 370 */ 371 public void formatMedia(String formatPath) throws IllegalStateException { 372 if (mContext.checkCallingOrSelfPermission( 373 android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS) 374 != PackageManager.PERMISSION_GRANTED) { 375 throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission"); 376 } 377 378 mListener.formatVolume(formatPath); 379 } 380 381 /** 382 * Returns true if we're playing media notification sounds. 383 */ 384 public boolean getPlayNotificationSounds() { 385 return mPlaySounds; 386 } 387 388 /** 389 * Set whether or not we're playing media notification sounds. 390 */ 391 public void setPlayNotificationSounds(boolean enabled) { 392 if (mContext.checkCallingOrSelfPermission( 393 android.Manifest.permission.WRITE_SETTINGS) 394 != PackageManager.PERMISSION_GRANTED) { 395 throw new SecurityException("Requires WRITE_SETTINGS permission"); 396 } 397 mPlaySounds = enabled; 398 SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0")); 399 } 400 401 void updatePublicVolumeState(String mountPoint, String state) { 402 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) { 403 Log.w(TAG, "Multiple volumes not currently supported"); 404 return; 405 } 406 Log.w(TAG, "State for {" + mountPoint + "} = {" + state + "}"); 407 mLegacyState = state; 408 } 409 410 /** 411 * Update the state of the USB mass storage notification 412 */ 413 void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) { 414 415 try { 416 417 if (getMassStorageConnected() && !suppressIfConnected) { 418 Intent intent = new Intent(); 419 intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class); 420 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 421 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 422 setUsbStorageNotification( 423 com.android.internal.R.string.usb_storage_notification_title, 424 com.android.internal.R.string.usb_storage_notification_message, 425 com.android.internal.R.drawable.stat_sys_data_usb, 426 sound, true, pi); 427 } else { 428 setUsbStorageNotification(0, 0, 0, false, false, null); 429 } 430 } catch (IllegalStateException e) { 431 // Nothing to do 432 } 433 } 434 435 void handlePossibleExplicitUnmountBroadcast(String path) { 436 if (mMounted) { 437 mMounted = false; 438 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 439 Uri.parse("file://" + path)); 440 mContext.sendBroadcast(intent); 441 } 442 } 443 444 void onVoldConnected() { 445 new Thread() { 446 public void run() { 447 try { 448 if (!getVolumeState(Environment.getExternalStorageDirectory().getPath()) 449 .equals(Environment.MEDIA_MOUNTED)) { 450 try { 451 mountMedia(Environment.getExternalStorageDirectory().getPath()); 452 } catch (Exception ex) { 453 Log.w(TAG, "Connection-mount failed"); 454 } 455 } else { 456 Log.d(TAG, "Skipping connection-mount; already mounted"); 457 } 458 } catch (IllegalStateException rex) { 459 Log.e(TAG, "Exception while handling connection mount ", rex); 460 } 461 462 try { 463 boolean avail = mListener.getShareAvailable("ums"); 464 notifyShareAvailabilityChange("ums", avail); 465 } catch (Exception ex) { 466 Log.w(TAG, "Failed to get share availability"); 467 } 468 } 469 }.start(); 470 } 471 472 void notifyVolumeStateChange(String label, String mountPoint, int oldState, 473 int newState) throws IllegalStateException { 474 String vs = getVolumeState(mountPoint); 475 476 if (newState == VolumeState.Init) { 477 } else if (newState == VolumeState.NoMedia) { 478 // NoMedia is handled via Disk Remove events 479 } else if (newState == VolumeState.Idle) { 480 // Don't notify if we're in BAD_REMOVAL, NOFS, or UNMOUNTABLE 481 if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) && 482 !vs.equals(Environment.MEDIA_NOFS) && 483 !vs.equals(Environment.MEDIA_UNMOUNTABLE)) { 484 notifyMediaUnmounted(mountPoint); 485 } 486 } else if (newState == VolumeState.Pending) { 487 } else if (newState == VolumeState.Checking) { 488 notifyMediaChecking(mountPoint); 489 } else if (newState == VolumeState.Mounted) { 490 notifyMediaMounted(mountPoint, false); 491 } else if (newState == VolumeState.Unmounting) { 492 notifyMediaUnmounting(mountPoint); 493 } else if (newState == VolumeState.Formatting) { 494 } else if (newState == VolumeState.Shared) { 495 notifyMediaShared(mountPoint, false); 496 } else if (newState == VolumeState.SharedMnt) { 497 notifyMediaShared(mountPoint, true); 498 } else { 499 Log.e(TAG, "Unhandled VolumeState {" + newState + "}"); 500 } 501 } 502 503 504 /** 505 * Broadcasts the USB mass storage connected event to all clients. 506 */ 507 void notifyUmsConnected() { 508 mUmsConnected = true; 509 510 String storageState = Environment.getExternalStorageState(); 511 if (!storageState.equals(Environment.MEDIA_REMOVED) && 512 !storageState.equals(Environment.MEDIA_BAD_REMOVAL) && 513 !storageState.equals(Environment.MEDIA_CHECKING)) { 514 515 if (mAutoStartUms) { 516 try { 517 setMassStorageEnabled(true); 518 } catch (IllegalStateException e) { 519 } 520 } else if (mPromptUms) { 521 updateUsbMassStorageNotification(false, true); 522 } 523 } 524 525 Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED); 526 mContext.sendBroadcast(intent); 527 } 528 529 void notifyShareAvailabilityChange(String method, final boolean avail) { 530 if (!method.equals("ums")) { 531 Log.w(TAG, "Ignoring unsupported share method {" + method + "}"); 532 return; 533 } 534 535 /* 536 * Notification needs to run in a different thread as 537 * it may need to call back into vold 538 */ 539 new Thread() { 540 public void run() { 541 try { 542 if (avail) { 543 notifyUmsConnected(); 544 } else { 545 notifyUmsDisconnected(); 546 } 547 } catch (Exception ex) { 548 Log.w(TAG, "Failed to mount media on insertion"); 549 } 550 } 551 }.start(); 552 } 553 554 /** 555 * Broadcasts the USB mass storage disconnected event to all clients. 556 */ 557 void notifyUmsDisconnected() { 558 mUmsConnected = false; 559 if (mUmsEnabled) { 560 try { 561 Log.w(TAG, "UMS disconnected while enabled!"); 562 setMassStorageEnabled(false); 563 } catch (Exception ex) { 564 Log.e(TAG, "Error disabling UMS on unsafe UMS disconnect", ex); 565 } 566 } 567 updateUsbMassStorageNotification(false, false); 568 Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED); 569 mContext.sendBroadcast(intent); 570 } 571 572 void notifyMediaInserted(final String path) throws IllegalStateException { 573 new Thread() { 574 public void run() { 575 try { 576 mountMedia(path); 577 } catch (Exception ex) { 578 Log.w(TAG, "Failed to mount media on insertion", ex); 579 } 580 } 581 }.start(); 582 } 583 584 /** 585 * Broadcasts the media removed event to all clients. 586 */ 587 void notifyMediaRemoved(String path) throws IllegalStateException { 588 589 // Suppress this on bad removal 590 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) { 591 return; 592 } 593 594 updatePublicVolumeState(path, Environment.MEDIA_REMOVED); 595 596 updateUsbMassStorageNotification(true, false); 597 598 setMediaStorageNotification( 599 com.android.internal.R.string.ext_media_nomedia_notification_title, 600 com.android.internal.R.string.ext_media_nomedia_notification_message, 601 com.android.internal.R.drawable.stat_notify_sdcard_usb, 602 true, false, null); 603 handlePossibleExplicitUnmountBroadcast(path); 604 605 Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED, 606 Uri.parse("file://" + path)); 607 mContext.sendBroadcast(intent); 608 } 609 610 /** 611 * Broadcasts the media unmounted event to all clients. 612 */ 613 void notifyMediaUnmounted(String path) { 614 615 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); 616 617 if (mShowSafeUnmountNotificationWhenUnmounted) { 618 setMediaStorageNotification( 619 com.android.internal.R.string.ext_media_safe_unmount_notification_title, 620 com.android.internal.R.string.ext_media_safe_unmount_notification_message, 621 com.android.internal.R.drawable.stat_notify_sdcard, 622 true, true, null); 623 mShowSafeUnmountNotificationWhenUnmounted = false; 624 } else { 625 setMediaStorageNotification(0, 0, 0, false, false, null); 626 } 627 updateUsbMassStorageNotification(false, false); 628 629 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 630 Uri.parse("file://" + path)); 631 mContext.sendBroadcast(intent); 632 } 633 634 /** 635 * Broadcasts the media checking event to all clients. 636 */ 637 void notifyMediaChecking(String path) { 638 updatePublicVolumeState(path, Environment.MEDIA_CHECKING); 639 640 setMediaStorageNotification( 641 com.android.internal.R.string.ext_media_checking_notification_title, 642 com.android.internal.R.string.ext_media_checking_notification_message, 643 com.android.internal.R.drawable.stat_notify_sdcard_prepare, 644 true, false, null); 645 646 updateUsbMassStorageNotification(true, false); 647 Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING, 648 Uri.parse("file://" + path)); 649 mContext.sendBroadcast(intent); 650 } 651 652 /** 653 * Broadcasts the media nofs event to all clients. 654 */ 655 void notifyMediaNoFs(String path) { 656 updatePublicVolumeState(path, Environment.MEDIA_NOFS); 657 658 Intent intent = new Intent(); 659 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); 660 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 661 662 setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title, 663 com.android.internal.R.string.ext_media_nofs_notification_message, 664 com.android.internal.R.drawable.stat_notify_sdcard_usb, 665 true, false, pi); 666 updateUsbMassStorageNotification(false, false); 667 intent = new Intent(Intent.ACTION_MEDIA_NOFS, 668 Uri.parse("file://" + path)); 669 mContext.sendBroadcast(intent); 670 } 671 672 /** 673 * Broadcasts the media mounted event to all clients. 674 */ 675 void notifyMediaMounted(String path, boolean readOnly) { 676 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED); 677 678 setMediaStorageNotification(0, 0, 0, false, false, null); 679 updateUsbMassStorageNotification(false, false); 680 Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, 681 Uri.parse("file://" + path)); 682 intent.putExtra("read-only", readOnly); 683 mMounted = true; 684 mContext.sendBroadcast(intent); 685 } 686 687 /** 688 * Broadcasts the media shared event to all clients. 689 */ 690 void notifyMediaShared(String path, boolean mounted) { 691 if (mounted) { 692 Log.e(TAG, "Live shared mounts not supported yet!"); 693 return; 694 } 695 696 updatePublicVolumeState(path, Environment.MEDIA_SHARED); 697 698 if (mUmsActiveNotify) { 699 Intent intent = new Intent(); 700 intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class); 701 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 702 setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title, 703 com.android.internal.R.string.usb_storage_stop_notification_message, 704 com.android.internal.R.drawable.stat_sys_warning, 705 false, true, pi); 706 } 707 handlePossibleExplicitUnmountBroadcast(path); 708 Intent intent = new Intent(Intent.ACTION_MEDIA_SHARED, 709 Uri.parse("file://" + path)); 710 mContext.sendBroadcast(intent); 711 } 712 713 /** 714 * Broadcasts the media bad removal event to all clients. 715 */ 716 void notifyMediaBadRemoval(String path) { 717 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL); 718 719 updateUsbMassStorageNotification(true, false); 720 setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title, 721 com.android.internal.R.string.ext_media_badremoval_notification_message, 722 com.android.internal.R.drawable.stat_sys_warning, 723 true, true, null); 724 725 handlePossibleExplicitUnmountBroadcast(path); 726 Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, 727 Uri.parse("file://" + path)); 728 mContext.sendBroadcast(intent); 729 } 730 731 /** 732 * Broadcasts the media unmountable event to all clients. 733 */ 734 void notifyMediaUnmountable(String path) { 735 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE); 736 737 Intent intent = new Intent(); 738 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); 739 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 740 741 setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title, 742 com.android.internal.R.string.ext_media_unmountable_notification_message, 743 com.android.internal.R.drawable.stat_notify_sdcard_usb, 744 true, false, pi); 745 updateUsbMassStorageNotification(false, false); 746 747 handlePossibleExplicitUnmountBroadcast(path); 748 749 intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, 750 Uri.parse("file://" + path)); 751 mContext.sendBroadcast(intent); 752 } 753 754 /** 755 * Broadcasts the media eject event to all clients. 756 */ 757 void notifyMediaUnmounting(String path) { 758 Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT, 759 Uri.parse("file://" + path)); 760 mContext.sendBroadcast(intent); 761 } 762 763 /** 764 * Sets the USB storage notification. 765 */ 766 private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible, 767 PendingIntent pi) { 768 769 if (!visible && mUsbStorageNotification == null) { 770 return; 771 } 772 773 NotificationManager notificationManager = (NotificationManager) mContext 774 .getSystemService(Context.NOTIFICATION_SERVICE); 775 776 if (notificationManager == null) { 777 return; 778 } 779 780 if (visible) { 781 Resources r = Resources.getSystem(); 782 CharSequence title = r.getText(titleId); 783 CharSequence message = r.getText(messageId); 784 785 if (mUsbStorageNotification == null) { 786 mUsbStorageNotification = new Notification(); 787 mUsbStorageNotification.icon = icon; 788 mUsbStorageNotification.when = 0; 789 } 790 791 if (sound && mPlaySounds) { 792 mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; 793 } else { 794 mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; 795 } 796 797 mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; 798 799 mUsbStorageNotification.tickerText = title; 800 if (pi == null) { 801 Intent intent = new Intent(); 802 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); 803 } 804 805 mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi); 806 } 807 808 final int notificationId = mUsbStorageNotification.icon; 809 if (visible) { 810 notificationManager.notify(notificationId, mUsbStorageNotification); 811 } else { 812 notificationManager.cancel(notificationId); 813 } 814 } 815 816 private synchronized boolean getMediaStorageNotificationDismissable() { 817 if ((mMediaStorageNotification != null) && 818 ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) == 819 Notification.FLAG_AUTO_CANCEL)) 820 return true; 821 822 return false; 823 } 824 825 /** 826 * Sets the media storage notification. 827 */ 828 private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible, 829 boolean dismissable, PendingIntent pi) { 830 831 if (!visible && mMediaStorageNotification == null) { 832 return; 833 } 834 835 NotificationManager notificationManager = (NotificationManager) mContext 836 .getSystemService(Context.NOTIFICATION_SERVICE); 837 838 if (notificationManager == null) { 839 return; 840 } 841 842 if (mMediaStorageNotification != null && visible) { 843 /* 844 * Dismiss the previous notification - we're about to 845 * re-use it. 846 */ 847 final int notificationId = mMediaStorageNotification.icon; 848 notificationManager.cancel(notificationId); 849 } 850 851 if (visible) { 852 Resources r = Resources.getSystem(); 853 CharSequence title = r.getText(titleId); 854 CharSequence message = r.getText(messageId); 855 856 if (mMediaStorageNotification == null) { 857 mMediaStorageNotification = new Notification(); 858 mMediaStorageNotification.when = 0; 859 } 860 861 if (mPlaySounds) { 862 mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND; 863 } else { 864 mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; 865 } 866 867 if (dismissable) { 868 mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; 869 } else { 870 mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; 871 } 872 873 mMediaStorageNotification.tickerText = title; 874 if (pi == null) { 875 Intent intent = new Intent(); 876 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); 877 } 878 879 mMediaStorageNotification.icon = icon; 880 mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi); 881 } 882 883 final int notificationId = mMediaStorageNotification.icon; 884 if (visible) { 885 notificationManager.notify(notificationId, mMediaStorageNotification); 886 } else { 887 notificationManager.cancel(notificationId); 888 } 889 } 890 891 public String[] getSecureContainerList() throws IllegalStateException { 892 return mListener.listAsec(); 893 } 894 895 public String createSecureContainer(String id, int sizeMb, String fstype, 896 String key, int ownerUid) throws IllegalStateException { 897 return mListener.createAsec(id, sizeMb, fstype, key, ownerUid); 898 } 899 900 public void finalizeSecureContainer(String id) throws IllegalStateException { 901 mListener.finalizeAsec(id); 902 } 903 904 public void destroySecureContainer(String id) throws IllegalStateException { 905 mListener.destroyAsec(id); 906 } 907 908 public String mountSecureContainer(String id, String key, int ownerUid) throws IllegalStateException { 909 return mListener.mountAsec(id, key, ownerUid); 910 } 911 912 public String getSecureContainerPath(String id) throws IllegalStateException { 913 return mListener.getAsecPath(id); 914 } 915 916} 917 918