MountService.java revision dde25397985d82352965c54d68c0ee181b2c3cc8
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.text.TextUtils; 34import android.util.Log; 35 36import java.io.File; 37import java.io.FileReader; 38import java.lang.IllegalStateException; 39 40/** 41 * MountService implements an to the mount service daemon 42 * @hide 43 */ 44class MountService extends IMountService.Stub { 45 46 private static final String TAG = "MountService"; 47 48 class VolumeState { 49 public static final int Init = -1; 50 public static final int NoMedia = 0; 51 public static final int Idle = 1; 52 public static final int Pending = 2; 53 public static final int Checking = 3; 54 public static final int Mounted = 4; 55 public static final int Unmounting = 5; 56 public static final int Formatting = 6; 57 public static final int Shared = 7; 58 public static final int SharedMnt = 8; 59 } 60 61 /** 62 * Binder context for this service 63 */ 64 private Context mContext; 65 66 /** 67 * listener object for communicating with the mount service daemon 68 */ 69 private MountListener mListener; 70 71 /** 72 * The notification that is shown when a USB mass storage host 73 * is connected. 74 * <p> 75 * This is lazily created, so use {@link #setUsbStorageNotification()}. 76 */ 77 private Notification mUsbStorageNotification; 78 79 80 /** 81 * The notification that is shown when the following media events occur: 82 * - Media is being checked 83 * - Media is blank (or unknown filesystem) 84 * - Media is corrupt 85 * - Media is safe to unmount 86 * - Media is missing 87 * <p> 88 * This is lazily created, so use {@link #setMediaStorageNotification()}. 89 */ 90 private Notification mMediaStorageNotification; 91 92 private boolean mShowSafeUnmountNotificationWhenUnmounted; 93 94 private boolean mPlaySounds; 95 96 private boolean mMounted; 97 98 private boolean mAutoStartUms; 99 100 private boolean mUmsConnected = false; 101 private boolean mUmsEnabled = false; 102 103 private String mLegacyState = Environment.MEDIA_REMOVED; 104 105 /** 106 * Constructs a new MountService instance 107 * 108 * @param context Binder context for this service 109 */ 110 public MountService(Context context) { 111 mContext = context; 112 113 // Register a BOOT_COMPLETED handler so that we can start 114 // MountListener. We defer the startup so that we don't 115 // start processing events before we ought-to 116 mContext.registerReceiver(mBroadcastReceiver, 117 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); 118 119 mListener = new MountListener(this); 120 mShowSafeUnmountNotificationWhenUnmounted = false; 121 122 mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1"); 123 124 mAutoStartUms = SystemProperties.get("persist.service.mount.umsauto", "0").equals("1"); 125 } 126 127 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 128 public void onReceive(Context context, Intent intent) { 129 String action = intent.getAction(); 130 131 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { 132 Thread thread = new Thread(mListener, MountListener.class.getName()); 133 thread.start(); 134 } 135 } 136 }; 137 138 public void shutdown() { 139 if (mContext.checkCallingOrSelfPermission( 140 android.Manifest.permission.SHUTDOWN) 141 != PackageManager.PERMISSION_GRANTED) { 142 throw new SecurityException("Requires SHUTDOWN permission"); 143 } 144 145 Log.d(TAG, "Shutting down"); 146 String state = Environment.getExternalStorageState(); 147 148 if (state.equals(Environment.MEDIA_SHARED)) { 149 /* 150 * If the media is currently shared, unshare it. 151 * XXX: This is still dangerous!. We should not 152 * be rebooting at *all* if UMS is enabled, since 153 * the UMS host could have dirty FAT cache entries 154 * yet to flush. 155 */ 156 try { 157 setMassStorageEnabled(false); 158 } catch (Exception e) { 159 Log.e(TAG, "ums disable failed", e); 160 } 161 } else if (state.equals(Environment.MEDIA_CHECKING)) { 162 /* 163 * If the media is being checked, then we need to wait for 164 * it to complete before being able to proceed. 165 */ 166 // XXX: @hackbod - Should we disable the ANR timer here? 167 int retries = 30; 168 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) { 169 try { 170 Thread.sleep(1000); 171 } catch (InterruptedException iex) { 172 Log.e(TAG, "Interrupted while waiting for media", iex); 173 break; 174 } 175 state = Environment.getExternalStorageState(); 176 } 177 if (retries == 0) { 178 Log.e(TAG, "Timed out waiting for media to check"); 179 } 180 } 181 182 if (state.equals(Environment.MEDIA_MOUNTED)) { 183 /* 184 * If the media is mounted, then gracefully unmount it. 185 */ 186 try { 187 String m = Environment.getExternalStorageDirectory().toString(); 188 unmountMedia(m); 189 } catch (Exception e) { 190 Log.e(TAG, "external storage unmount failed", e); 191 } 192 } 193 } 194 195 /** 196 * @return true if USB mass storage support is enabled. 197 */ 198 public boolean getMassStorageEnabled() { 199 return mUmsEnabled; 200 } 201 202 /** 203 * Enables or disables USB mass storage support. 204 * 205 * @param enable true to enable USB mass storage support 206 */ 207 public void setMassStorageEnabled(boolean enable) throws IllegalStateException { 208 try { 209 String vp = Environment.getExternalStorageDirectory().getPath(); 210 String vs = getVolumeState(vp); 211 212 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) { 213 Log.d(TAG, "Unmounting media before UMS enable"); 214 unmountMedia(vp); 215 } 216 217 mListener.setShareMethodEnabled(Environment 218 .getExternalStorageDirectory() 219 .getPath(), 220 "ums", enable); 221 mUmsEnabled = enable; 222 if (!enable) { 223 Log.d(TAG, "Mounting media after UMS disable"); 224 mountMedia(vp); 225 } 226 } catch (IllegalStateException rex) { 227 Log.e(TAG, "Failed to set ums enable {" + enable + "}"); 228 return; 229 } 230 } 231 232 /** 233 * @return true if USB mass storage is connected. 234 */ 235 public boolean getMassStorageConnected() { 236 return mUmsConnected; 237 } 238 239 /** 240 * @return state of the volume at the specified mount point 241 */ 242 public String getVolumeState(String mountPoint) throws IllegalStateException { 243 /* 244 * XXX: Until we have multiple volume discovery, just hardwire 245 * this to /sdcard 246 */ 247 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) { 248 Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume"); 249 throw new IllegalArgumentException(); 250 } 251 252 return mLegacyState; 253 } 254 255 256 /** 257 * Attempt to mount external media 258 */ 259 public void mountMedia(String mountPath) throws IllegalStateException { 260 if (mContext.checkCallingOrSelfPermission( 261 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 262 != PackageManager.PERMISSION_GRANTED) { 263 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); 264 } 265 mListener.mountVolume(mountPath); 266 } 267 268 /** 269 * Attempt to unmount external media to prepare for eject 270 */ 271 public void unmountMedia(String mountPath) throws IllegalStateException { 272 if (mContext.checkCallingOrSelfPermission( 273 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 274 != PackageManager.PERMISSION_GRANTED) { 275 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); 276 } 277 278 // Set a flag so that when we get the unmounted event, we know 279 // to display the notification 280 mShowSafeUnmountNotificationWhenUnmounted = true; 281 282 // tell mountd to unmount the media 283 mListener.unmountVolume(mountPath); 284 } 285 286 /** 287 * Attempt to format external media 288 */ 289 public void formatMedia(String formatPath) throws IllegalStateException { 290 if (mContext.checkCallingOrSelfPermission( 291 android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS) 292 != PackageManager.PERMISSION_GRANTED) { 293 throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission"); 294 } 295 296 mListener.formatVolume(formatPath); 297 } 298 299 /** 300 * Returns true if we're playing media notification sounds. 301 */ 302 public boolean getPlayNotificationSounds() { 303 return mPlaySounds; 304 } 305 306 /** 307 * Set whether or not we're playing media notification sounds. 308 */ 309 public void setPlayNotificationSounds(boolean enabled) { 310 if (mContext.checkCallingOrSelfPermission( 311 android.Manifest.permission.WRITE_SETTINGS) 312 != PackageManager.PERMISSION_GRANTED) { 313 throw new SecurityException("Requires WRITE_SETTINGS permission"); 314 } 315 mPlaySounds = enabled; 316 SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0")); 317 } 318 319 /** 320 * Returns true if we auto-start UMS on cable insertion. 321 */ 322 public boolean getAutoStartUms() { 323 return mAutoStartUms; 324 } 325 326 /** 327 * Set whether or not we're playing media notification sounds. 328 */ 329 public void setAutoStartUms(boolean enabled) { 330 if (mContext.checkCallingOrSelfPermission( 331 android.Manifest.permission.WRITE_SETTINGS) 332 != PackageManager.PERMISSION_GRANTED) { 333 throw new SecurityException("Requires WRITE_SETTINGS permission"); 334 } 335 mAutoStartUms = enabled; 336 SystemProperties.set("persist.service.mount.umsauto", (enabled ? "1" : "0")); 337 } 338 339 void updatePublicVolumeState(String mountPoint, String state) { 340 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) { 341 Log.w(TAG, "Multiple volumes not currently supported"); 342 return; 343 } 344 Log.w(TAG, "State for {" + mountPoint + "} = {" + state + "}"); 345 mLegacyState = state; 346 } 347 348 /** 349 * Update the state of the USB mass storage notification 350 */ 351 void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) { 352 353 try { 354 355 if (getMassStorageConnected() && !suppressIfConnected) { 356 Intent intent = new Intent(); 357 intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class); 358 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 359 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 360 setUsbStorageNotification( 361 com.android.internal.R.string.usb_storage_notification_title, 362 com.android.internal.R.string.usb_storage_notification_message, 363 com.android.internal.R.drawable.stat_sys_data_usb, 364 sound, true, pi); 365 } else { 366 setUsbStorageNotification(0, 0, 0, false, false, null); 367 } 368 } catch (IllegalStateException e) { 369 // Nothing to do 370 } 371 } 372 373 void handlePossibleExplicitUnmountBroadcast(String path) { 374 if (mMounted) { 375 mMounted = false; 376 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 377 Uri.parse("file://" + path)); 378 mContext.sendBroadcast(intent); 379 } 380 } 381 382 void onVoldConnected() { 383 new Thread() { 384 public void run() { 385 try { 386 if (!getVolumeState(Environment.getExternalStorageDirectory().getPath()) 387 .equals(Environment.MEDIA_MOUNTED)) { 388 try { 389 mountMedia(Environment.getExternalStorageDirectory().getPath()); 390 Log.d(TAG, "Connection-mount suceeded"); 391 } catch (Exception ex) { 392 Log.w(TAG, "Connection-mount failed"); 393 } 394 } else { 395 Log.d(TAG, "Skipping connection-mount; already mounted"); 396 } 397 } catch (IllegalStateException rex) { 398 Log.e(TAG, "Exception while handling connection mount " + rex); 399 } 400 401 try { 402 boolean avail = mListener.getShareAvailable("ums"); 403 notifyShareAvailabilityChange("ums", avail); 404 } catch (Exception ex) { 405 Log.w(TAG, "Failed to get share availability"); 406 } 407 } 408 }.start(); 409 } 410 411 void notifyVolumeStateChange(String label, String mountPoint, int oldState, 412 int newState) throws IllegalStateException { 413 String vs = getVolumeState(mountPoint); 414 415 if (newState == VolumeState.Init) { 416 } else if (newState == VolumeState.NoMedia) { 417 // NoMedia is handled via Disk Remove events 418 } else if (newState == VolumeState.Idle) { 419 // Don't notify if we're in BAD_REMOVAL, NOFS, or UNMOUNTABLE 420 if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) && 421 !vs.equals(Environment.MEDIA_NOFS) && 422 !vs.equals(Environment.MEDIA_UNMOUNTABLE)) { 423 notifyMediaUnmounted(mountPoint); 424 } 425 } else if (newState == VolumeState.Pending) { 426 } else if (newState == VolumeState.Checking) { 427 notifyMediaChecking(mountPoint); 428 } else if (newState == VolumeState.Mounted) { 429 notifyMediaMounted(mountPoint, false); 430 } else if (newState == VolumeState.Unmounting) { 431 notifyMediaUnmounting(mountPoint); 432 } else if (newState == VolumeState.Formatting) { 433 } else if (newState == VolumeState.Shared) { 434 notifyMediaShared(mountPoint, false); 435 } else if (newState == VolumeState.SharedMnt) { 436 notifyMediaShared(mountPoint, true); 437 } else { 438 Log.e(TAG, "Unhandled VolumeState {" + newState + "}"); 439 } 440 } 441 442 443 /** 444 * Broadcasts the USB mass storage connected event to all clients. 445 */ 446 void notifyUmsConnected() { 447 mUmsConnected = true; 448 449 String storageState = Environment.getExternalStorageState(); 450 if (!storageState.equals(Environment.MEDIA_REMOVED) && 451 !storageState.equals(Environment.MEDIA_BAD_REMOVAL) && 452 !storageState.equals(Environment.MEDIA_CHECKING)) { 453 454 if (mAutoStartUms) { 455 try { 456 setMassStorageEnabled(true); 457 } catch (IllegalStateException e) { 458 } 459 } else { 460 updateUsbMassStorageNotification(false, true); 461 } 462 } 463 464 Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED); 465 mContext.sendBroadcast(intent); 466 } 467 468 void notifyShareAvailabilityChange(String method, boolean avail) { 469 Log.d(TAG, "Share method {" + method + "} availability now " + avail); 470 if (!method.equals("ums")) { 471 Log.w(TAG, "Ignoring unsupported share method {" + method + "}"); 472 return; 473 } 474 if (avail) { 475 notifyUmsConnected(); 476 } else { 477 notifyUmsDisconnected(); 478 } 479 } 480 481 /** 482 * Broadcasts the USB mass storage disconnected event to all clients. 483 */ 484 void notifyUmsDisconnected() { 485 mUmsConnected = false; 486 updateUsbMassStorageNotification(false, false); 487 Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED); 488 mContext.sendBroadcast(intent); 489 } 490 491 void notifyMediaInserted(final String path) throws IllegalStateException { 492 new Thread() { 493 public void run() { 494 try { 495 Log.d(TAG, "Mounting media after insertion"); 496 mountMedia(path); 497 } catch (Exception ex) { 498 Log.w(TAG, "Failed to mount media on insertion"); 499 } 500 } 501 }.start(); 502 } 503 504 /** 505 * Broadcasts the media removed event to all clients. 506 */ 507 void notifyMediaRemoved(String path) throws IllegalStateException { 508 509 // Suppress this on bad removal 510 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) { 511 return; 512 } 513 514 updatePublicVolumeState(path, Environment.MEDIA_REMOVED); 515 516 updateUsbMassStorageNotification(true, false); 517 518 setMediaStorageNotification( 519 com.android.internal.R.string.ext_media_nomedia_notification_title, 520 com.android.internal.R.string.ext_media_nomedia_notification_message, 521 com.android.internal.R.drawable.stat_notify_sdcard_usb, 522 true, false, null); 523 handlePossibleExplicitUnmountBroadcast(path); 524 525 // Log.d(TAG, "Sending ACTION_MEDIA_REMOVED"); 526 Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED, 527 Uri.parse("file://" + path)); 528 mContext.sendBroadcast(intent); 529 } 530 531 /** 532 * Broadcasts the media unmounted event to all clients. 533 */ 534 void notifyMediaUnmounted(String path) { 535 536 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); 537 538 if (mShowSafeUnmountNotificationWhenUnmounted) { 539 setMediaStorageNotification( 540 com.android.internal.R.string.ext_media_safe_unmount_notification_title, 541 com.android.internal.R.string.ext_media_safe_unmount_notification_message, 542 com.android.internal.R.drawable.stat_notify_sdcard, 543 true, true, null); 544 mShowSafeUnmountNotificationWhenUnmounted = false; 545 } else { 546 setMediaStorageNotification(0, 0, 0, false, false, null); 547 } 548 updateUsbMassStorageNotification(false, false); 549 550 // Log.d(TAG, "Sending ACTION_MEDIA_UNMOUNTED"); 551 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 552 Uri.parse("file://" + path)); 553 mContext.sendBroadcast(intent); 554 } 555 556 /** 557 * Broadcasts the media checking event to all clients. 558 */ 559 void notifyMediaChecking(String path) { 560 updatePublicVolumeState(path, Environment.MEDIA_CHECKING); 561 562 setMediaStorageNotification( 563 com.android.internal.R.string.ext_media_checking_notification_title, 564 com.android.internal.R.string.ext_media_checking_notification_message, 565 com.android.internal.R.drawable.stat_notify_sdcard_prepare, 566 true, false, null); 567 568 updateUsbMassStorageNotification(true, false); 569 // Log.d(TAG, "Sending ACTION_MEDIA_CHECKING"); 570 Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING, 571 Uri.parse("file://" + path)); 572 mContext.sendBroadcast(intent); 573 } 574 575 /** 576 * Broadcasts the media nofs event to all clients. 577 */ 578 void notifyMediaNoFs(String path) { 579 updatePublicVolumeState(path, Environment.MEDIA_NOFS); 580 581 Intent intent = new Intent(); 582 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); 583 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 584 585 setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title, 586 com.android.internal.R.string.ext_media_nofs_notification_message, 587 com.android.internal.R.drawable.stat_notify_sdcard_usb, 588 true, false, pi); 589 updateUsbMassStorageNotification(false, false); 590 // Log.d(TAG, "Sending ACTION_MEDIA_NOFS"); 591 intent = new Intent(Intent.ACTION_MEDIA_NOFS, 592 Uri.parse("file://" + path)); 593 mContext.sendBroadcast(intent); 594 } 595 596 /** 597 * Broadcasts the media mounted event to all clients. 598 */ 599 void notifyMediaMounted(String path, boolean readOnly) { 600 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED); 601 602 setMediaStorageNotification(0, 0, 0, false, false, null); 603 updateUsbMassStorageNotification(false, false); 604 // Log.d(TAG, "Sending ACTION_MEDIA_MOUNTED"); 605 Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, 606 Uri.parse("file://" + path)); 607 intent.putExtra("read-only", readOnly); 608 mMounted = true; 609 mContext.sendBroadcast(intent); 610 } 611 612 /** 613 * Broadcasts the media shared event to all clients. 614 */ 615 void notifyMediaShared(String path, boolean mounted) { 616 if (mounted) { 617 Log.e(TAG, "Live shared mounts not supported yet!"); 618 return; 619 } 620 621 updatePublicVolumeState(path, Environment.MEDIA_SHARED); 622 623 Intent intent = new Intent(); 624 intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class); 625 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 626 setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title, 627 com.android.internal.R.string.usb_storage_stop_notification_message, 628 com.android.internal.R.drawable.stat_sys_warning, 629 false, true, pi); 630 handlePossibleExplicitUnmountBroadcast(path); 631 // Log.d(TAG, "Sending ACTION_MEDIA_SHARED"); 632 intent = new Intent(Intent.ACTION_MEDIA_SHARED, 633 Uri.parse("file://" + path)); 634 mContext.sendBroadcast(intent); 635 } 636 637 /** 638 * Broadcasts the media bad removal event to all clients. 639 */ 640 void notifyMediaBadRemoval(String path) { 641 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL); 642 643 updateUsbMassStorageNotification(true, false); 644 setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title, 645 com.android.internal.R.string.ext_media_badremoval_notification_message, 646 com.android.internal.R.drawable.stat_sys_warning, 647 true, true, null); 648 649 handlePossibleExplicitUnmountBroadcast(path); 650 // Log.d(TAG, "Sending ACTION_MEDIA_BAD_REMOVAL"); 651 Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, 652 Uri.parse("file://" + path)); 653 mContext.sendBroadcast(intent); 654 } 655 656 /** 657 * Broadcasts the media unmountable event to all clients. 658 */ 659 void notifyMediaUnmountable(String path) { 660 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE); 661 662 Intent intent = new Intent(); 663 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); 664 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 665 666 setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title, 667 com.android.internal.R.string.ext_media_unmountable_notification_message, 668 com.android.internal.R.drawable.stat_notify_sdcard_usb, 669 true, false, pi); 670 updateUsbMassStorageNotification(false, false); 671 672 handlePossibleExplicitUnmountBroadcast(path); 673 674 // Log.d(TAG, "Sending ACTION_MEDIA_UNMOUNTABLE"); 675 intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, 676 Uri.parse("file://" + path)); 677 mContext.sendBroadcast(intent); 678 } 679 680 /** 681 * Broadcasts the media eject event to all clients. 682 */ 683 void notifyMediaUnmounting(String path) { 684 // Log.d(TAG, "Sending ACTION_MEDIA_EJECT"); 685 Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT, 686 Uri.parse("file://" + path)); 687 mContext.sendBroadcast(intent); 688 } 689 690 /** 691 * Sets the USB storage notification. 692 */ 693 private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible, 694 PendingIntent pi) { 695 696 if (!visible && mUsbStorageNotification == null) { 697 return; 698 } 699 700 NotificationManager notificationManager = (NotificationManager) mContext 701 .getSystemService(Context.NOTIFICATION_SERVICE); 702 703 if (notificationManager == null) { 704 return; 705 } 706 707 if (visible) { 708 Resources r = Resources.getSystem(); 709 CharSequence title = r.getText(titleId); 710 CharSequence message = r.getText(messageId); 711 712 if (mUsbStorageNotification == null) { 713 mUsbStorageNotification = new Notification(); 714 mUsbStorageNotification.icon = icon; 715 mUsbStorageNotification.when = 0; 716 } 717 718 if (sound && mPlaySounds) { 719 mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; 720 } else { 721 mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; 722 } 723 724 mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; 725 726 mUsbStorageNotification.tickerText = title; 727 if (pi == null) { 728 Intent intent = new Intent(); 729 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); 730 } 731 732 mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi); 733 } 734 735 final int notificationId = mUsbStorageNotification.icon; 736 if (visible) { 737 notificationManager.notify(notificationId, mUsbStorageNotification); 738 } else { 739 notificationManager.cancel(notificationId); 740 } 741 } 742 743 private synchronized boolean getMediaStorageNotificationDismissable() { 744 if ((mMediaStorageNotification != null) && 745 ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) == 746 Notification.FLAG_AUTO_CANCEL)) 747 return true; 748 749 return false; 750 } 751 752 /** 753 * Sets the media storage notification. 754 */ 755 private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible, 756 boolean dismissable, PendingIntent pi) { 757 758 if (!visible && mMediaStorageNotification == null) { 759 return; 760 } 761 762 NotificationManager notificationManager = (NotificationManager) mContext 763 .getSystemService(Context.NOTIFICATION_SERVICE); 764 765 if (notificationManager == null) { 766 return; 767 } 768 769 if (mMediaStorageNotification != null && visible) { 770 /* 771 * Dismiss the previous notification - we're about to 772 * re-use it. 773 */ 774 final int notificationId = mMediaStorageNotification.icon; 775 notificationManager.cancel(notificationId); 776 } 777 778 if (visible) { 779 Resources r = Resources.getSystem(); 780 CharSequence title = r.getText(titleId); 781 CharSequence message = r.getText(messageId); 782 783 if (mMediaStorageNotification == null) { 784 mMediaStorageNotification = new Notification(); 785 mMediaStorageNotification.when = 0; 786 } 787 788 if (mPlaySounds) { 789 mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND; 790 } else { 791 mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; 792 } 793 794 if (dismissable) { 795 mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; 796 } else { 797 mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; 798 } 799 800 mMediaStorageNotification.tickerText = title; 801 if (pi == null) { 802 Intent intent = new Intent(); 803 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); 804 } 805 806 mMediaStorageNotification.icon = icon; 807 mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi); 808 } 809 810 final int notificationId = mMediaStorageNotification.icon; 811 if (visible) { 812 notificationManager.notify(notificationId, mMediaStorageNotification); 813 } else { 814 notificationManager.cancel(notificationId); 815 } 816 } 817 818 public String[] getSecureCacheList() throws IllegalStateException { 819 return mListener.listAsec(); 820 } 821 822 public String createSecureCache(String id, int sizeMb, String fstype, 823 String key, int ownerUid) throws IllegalStateException { 824 return mListener.createAsec(id, sizeMb, fstype, key, ownerUid); 825 } 826 827 public void finalizeSecureCache(String id) throws IllegalStateException { 828 mListener.finalizeAsec(id); 829 } 830 831 public void destroySecureCache(String id) throws IllegalStateException { 832 mListener.destroyAsec(id); 833 } 834 835 public String mountSecureCache(String id, String key, int ownerUid) throws IllegalStateException { 836 return mListener.mountAsec(id, key, ownerUid); 837 } 838 839 public String getSecureCachePath(String id) throws IllegalStateException { 840 return mListener.getAsecPath(id); 841 } 842 843} 844 845