MountService.java revision 7fd0fee968f4a3e474e1ea9933fc03552fe5f50a
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.RemoteException; 32import android.os.SystemProperties; 33import android.os.UEventObserver; 34import android.text.TextUtils; 35import android.util.Log; 36 37import java.io.File; 38import java.io.FileReader; 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 if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { 130 Thread thread = new Thread(mListener, MountListener.class.getName()); 131 thread.start(); 132 } 133 } 134 }; 135 136 /** 137 * @return true if USB mass storage support is enabled. 138 */ 139 public boolean getMassStorageEnabled() throws RemoteException { 140 return mUmsEnabled; 141 } 142 143 /** 144 * Enables or disables USB mass storage support. 145 * 146 * @param enable true to enable USB mass storage support 147 */ 148 public void setMassStorageEnabled(boolean enable) throws RemoteException { 149 try { 150 String vp = Environment.getExternalStorageDirectory().getPath(); 151 String vs = getVolumeState(vp); 152 153 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) { 154 Log.d(TAG, "Unmounting media before UMS enable"); 155 unmountMedia(vp); 156 } 157 158 mListener.setShareMethodEnabled(Environment 159 .getExternalStorageDirectory() 160 .getPath(), 161 "ums", enable); 162 mUmsEnabled = enable; 163 if (!enable) { 164 Log.d(TAG, "Mounting media after UMS disable"); 165 mountMedia(vp); 166 } 167 } catch (RemoteException rex) { 168 Log.e(TAG, "Failed to set ums enable {" + enable + "}"); 169 return; 170 } 171 } 172 173 /** 174 * @return true if USB mass storage is connected. 175 */ 176 public boolean getMassStorageConnected() throws RemoteException { 177 return mUmsConnected; 178 } 179 180 /** 181 * @return state of the volume at the specified mount point 182 */ 183 public String getVolumeState(String mountPoint) throws RemoteException { 184 /* 185 * XXX: Until we have multiple volume discovery, just hardwire 186 * this to /sdcard 187 */ 188 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) { 189 Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume"); 190 throw new IllegalArgumentException(); 191 } 192 193 return mLegacyState; 194 } 195 196 197 /** 198 * Attempt to mount external media 199 */ 200 public void mountMedia(String mountPath) throws RemoteException { 201 if (mContext.checkCallingOrSelfPermission( 202 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 203 != PackageManager.PERMISSION_GRANTED) { 204 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); 205 } 206 mListener.mountVolume(mountPath); 207 } 208 209 /** 210 * Attempt to unmount external media to prepare for eject 211 */ 212 public void unmountMedia(String mountPath) throws RemoteException { 213 if (mContext.checkCallingOrSelfPermission( 214 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 215 != PackageManager.PERMISSION_GRANTED) { 216 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); 217 } 218 219 // Set a flag so that when we get the unmounted event, we know 220 // to display the notification 221 mShowSafeUnmountNotificationWhenUnmounted = true; 222 223 // tell mountd to unmount the media 224 mListener.unmountVolume(mountPath); 225 } 226 227 /** 228 * Attempt to format external media 229 */ 230 public void formatMedia(String formatPath) throws RemoteException { 231 if (mContext.checkCallingOrSelfPermission( 232 android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS) 233 != PackageManager.PERMISSION_GRANTED) { 234 throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission"); 235 } 236 237 mListener.formatVolume(formatPath); 238 } 239 240 /** 241 * Returns true if we're playing media notification sounds. 242 */ 243 public boolean getPlayNotificationSounds() { 244 return mPlaySounds; 245 } 246 247 /** 248 * Set whether or not we're playing media notification sounds. 249 */ 250 public void setPlayNotificationSounds(boolean enabled) { 251 if (mContext.checkCallingOrSelfPermission( 252 android.Manifest.permission.WRITE_SETTINGS) 253 != PackageManager.PERMISSION_GRANTED) { 254 throw new SecurityException("Requires WRITE_SETTINGS permission"); 255 } 256 mPlaySounds = enabled; 257 SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0")); 258 } 259 260 /** 261 * Returns true if we auto-start UMS on cable insertion. 262 */ 263 public boolean getAutoStartUms() { 264 return mAutoStartUms; 265 } 266 267 /** 268 * Set whether or not we're playing media notification sounds. 269 */ 270 public void setAutoStartUms(boolean enabled) { 271 if (mContext.checkCallingOrSelfPermission( 272 android.Manifest.permission.WRITE_SETTINGS) 273 != PackageManager.PERMISSION_GRANTED) { 274 throw new SecurityException("Requires WRITE_SETTINGS permission"); 275 } 276 mAutoStartUms = enabled; 277 SystemProperties.set("persist.service.mount.umsauto", (enabled ? "1" : "0")); 278 } 279 280 void updatePublicVolumeState(String mountPoint, String state) { 281 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) { 282 Log.w(TAG, "Multiple volumes not currently supported"); 283 return; 284 } 285 Log.w(TAG, "State for {" + mountPoint + "} = {" + state + "}"); 286 mLegacyState = state; 287 } 288 289 /** 290 * Update the state of the USB mass storage notification 291 */ 292 void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) { 293 294 try { 295 296 if (getMassStorageConnected() && !suppressIfConnected) { 297 Intent intent = new Intent(); 298 intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class); 299 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 300 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 301 setUsbStorageNotification( 302 com.android.internal.R.string.usb_storage_notification_title, 303 com.android.internal.R.string.usb_storage_notification_message, 304 com.android.internal.R.drawable.stat_sys_data_usb, 305 sound, true, pi); 306 } else { 307 setUsbStorageNotification(0, 0, 0, false, false, null); 308 } 309 } catch (RemoteException e) { 310 // Nothing to do 311 } 312 } 313 314 void handlePossibleExplicitUnmountBroadcast(String path) { 315 if (mMounted) { 316 mMounted = false; 317 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 318 Uri.parse("file://" + path)); 319 mContext.sendBroadcast(intent); 320 } 321 } 322 323 void onVoldConnected() { 324 new Thread() { 325 public void run() { 326 try { 327 if (!getVolumeState(Environment.getExternalStorageDirectory().getPath()) 328 .equals(Environment.MEDIA_MOUNTED)) { 329 try { 330 mountMedia(Environment.getExternalStorageDirectory().getPath()); 331 Log.d(TAG, "Connection-mount suceeded"); 332 } catch (Exception ex) { 333 Log.w(TAG, "Connection-mount failed"); 334 } 335 } else { 336 Log.d(TAG, "Skipping connection-mount; already mounted"); 337 } 338 } catch (RemoteException rex) { 339 Log.e(TAG, "Exception while handling connection mount " + rex); 340 } 341 342 try { 343 boolean avail = mListener.getShareAvailable("ums"); 344 notifyShareAvailabilityChange("ums", avail); 345 } catch (Exception ex) { 346 Log.w(TAG, "Failed to get share availability"); 347 } 348 } 349 }.start(); 350 } 351 352 void notifyVolumeStateChange(String label, String mountPoint, int oldState, 353 int newState) throws RemoteException { 354 String vs = getVolumeState(mountPoint); 355 356 if (newState == VolumeState.Init) { 357 } else if (newState == VolumeState.NoMedia) { 358 // NoMedia is handled via Disk Remove events 359 } else if (newState == VolumeState.Idle) { 360 // Don't notify if we're in BAD_REMOVAL, NOFS, or UNMOUNTABLE 361 if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) && 362 !vs.equals(Environment.MEDIA_NOFS) && 363 !vs.equals(Environment.MEDIA_UNMOUNTABLE)) { 364 notifyMediaUnmounted(mountPoint); 365 } 366 } else if (newState == VolumeState.Pending) { 367 } else if (newState == VolumeState.Checking) { 368 notifyMediaChecking(mountPoint); 369 } else if (newState == VolumeState.Mounted) { 370 notifyMediaMounted(mountPoint, false); 371 } else if (newState == VolumeState.Unmounting) { 372 notifyMediaUnmounting(mountPoint); 373 } else if (newState == VolumeState.Formatting) { 374 } else if (newState == VolumeState.Shared) { 375 notifyMediaShared(mountPoint, false); 376 } else if (newState == VolumeState.SharedMnt) { 377 notifyMediaShared(mountPoint, true); 378 } else { 379 Log.e(TAG, "Unhandled VolumeState {" + newState + "}"); 380 } 381 } 382 383 384 /** 385 * Broadcasts the USB mass storage connected event to all clients. 386 */ 387 void notifyUmsConnected() { 388 mUmsConnected = true; 389 390 String storageState = Environment.getExternalStorageState(); 391 if (!storageState.equals(Environment.MEDIA_REMOVED) && 392 !storageState.equals(Environment.MEDIA_BAD_REMOVAL) && 393 !storageState.equals(Environment.MEDIA_CHECKING)) { 394 395 if (mAutoStartUms) { 396 try { 397 setMassStorageEnabled(true); 398 } catch (RemoteException e) { 399 } 400 } else { 401 updateUsbMassStorageNotification(false, true); 402 } 403 } 404 405 Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED); 406 mContext.sendBroadcast(intent); 407 } 408 409 void notifyShareAvailabilityChange(String method, boolean avail) { 410 Log.d(TAG, "Share method {" + method + "} availability now " + avail); 411 if (!method.equals("ums")) { 412 Log.w(TAG, "Ignoring unsupported share method {" + method + "}"); 413 return; 414 } 415 if (avail) { 416 notifyUmsConnected(); 417 } else { 418 notifyUmsDisconnected(); 419 } 420 } 421 422 /** 423 * Broadcasts the USB mass storage disconnected event to all clients. 424 */ 425 void notifyUmsDisconnected() { 426 mUmsConnected = false; 427 updateUsbMassStorageNotification(false, false); 428 Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED); 429 mContext.sendBroadcast(intent); 430 } 431 432 void notifyMediaInserted(final String path) throws RemoteException { 433 new Thread() { 434 public void run() { 435 try { 436 Log.d(TAG, "Mounting media after insertion"); 437 mountMedia(path); 438 } catch (Exception ex) { 439 Log.w(TAG, "Failed to mount media on insertion"); 440 } 441 } 442 }.start(); 443 } 444 445 /** 446 * Broadcasts the media removed event to all clients. 447 */ 448 void notifyMediaRemoved(String path) throws RemoteException { 449 450 // Suppress this on bad removal 451 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) { 452 return; 453 } 454 455 updatePublicVolumeState(path, Environment.MEDIA_REMOVED); 456 457 updateUsbMassStorageNotification(true, false); 458 459 setMediaStorageNotification( 460 com.android.internal.R.string.ext_media_nomedia_notification_title, 461 com.android.internal.R.string.ext_media_nomedia_notification_message, 462 com.android.internal.R.drawable.stat_notify_sdcard_usb, 463 true, false, null); 464 handlePossibleExplicitUnmountBroadcast(path); 465 466 // Log.d(TAG, "Sending ACTION_MEDIA_REMOVED"); 467 Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED, 468 Uri.parse("file://" + path)); 469 mContext.sendBroadcast(intent); 470 } 471 472 /** 473 * Broadcasts the media unmounted event to all clients. 474 */ 475 void notifyMediaUnmounted(String path) { 476 477 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); 478 479 if (mShowSafeUnmountNotificationWhenUnmounted) { 480 setMediaStorageNotification( 481 com.android.internal.R.string.ext_media_safe_unmount_notification_title, 482 com.android.internal.R.string.ext_media_safe_unmount_notification_message, 483 com.android.internal.R.drawable.stat_notify_sdcard, 484 true, true, null); 485 mShowSafeUnmountNotificationWhenUnmounted = false; 486 } else { 487 setMediaStorageNotification(0, 0, 0, false, false, null); 488 } 489 updateUsbMassStorageNotification(false, false); 490 491 // Log.d(TAG, "Sending ACTION_MEDIA_UNMOUNTED"); 492 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 493 Uri.parse("file://" + path)); 494 mContext.sendBroadcast(intent); 495 } 496 497 /** 498 * Broadcasts the media checking event to all clients. 499 */ 500 void notifyMediaChecking(String path) { 501 updatePublicVolumeState(path, Environment.MEDIA_CHECKING); 502 503 setMediaStorageNotification( 504 com.android.internal.R.string.ext_media_checking_notification_title, 505 com.android.internal.R.string.ext_media_checking_notification_message, 506 com.android.internal.R.drawable.stat_notify_sdcard_prepare, 507 true, false, null); 508 509 updateUsbMassStorageNotification(true, false); 510 // Log.d(TAG, "Sending ACTION_MEDIA_CHECKING"); 511 Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING, 512 Uri.parse("file://" + path)); 513 mContext.sendBroadcast(intent); 514 } 515 516 /** 517 * Broadcasts the media nofs event to all clients. 518 */ 519 void notifyMediaNoFs(String path) { 520 updatePublicVolumeState(path, Environment.MEDIA_NOFS); 521 522 Intent intent = new Intent(); 523 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); 524 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 525 526 setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title, 527 com.android.internal.R.string.ext_media_nofs_notification_message, 528 com.android.internal.R.drawable.stat_notify_sdcard_usb, 529 true, false, pi); 530 updateUsbMassStorageNotification(false, false); 531 // Log.d(TAG, "Sending ACTION_MEDIA_NOFS"); 532 intent = new Intent(Intent.ACTION_MEDIA_NOFS, 533 Uri.parse("file://" + path)); 534 mContext.sendBroadcast(intent); 535 } 536 537 /** 538 * Broadcasts the media mounted event to all clients. 539 */ 540 void notifyMediaMounted(String path, boolean readOnly) { 541 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED); 542 543 setMediaStorageNotification(0, 0, 0, false, false, null); 544 updateUsbMassStorageNotification(false, false); 545 // Log.d(TAG, "Sending ACTION_MEDIA_MOUNTED"); 546 Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, 547 Uri.parse("file://" + path)); 548 intent.putExtra("read-only", readOnly); 549 mMounted = true; 550 mContext.sendBroadcast(intent); 551 } 552 553 /** 554 * Broadcasts the media shared event to all clients. 555 */ 556 void notifyMediaShared(String path, boolean mounted) { 557 if (mounted) { 558 Log.e(TAG, "Live shared mounts not supported yet!"); 559 return; 560 } 561 562 updatePublicVolumeState(path, Environment.MEDIA_SHARED); 563 564 Intent intent = new Intent(); 565 intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class); 566 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 567 setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title, 568 com.android.internal.R.string.usb_storage_stop_notification_message, 569 com.android.internal.R.drawable.stat_sys_warning, 570 false, true, pi); 571 handlePossibleExplicitUnmountBroadcast(path); 572 // Log.d(TAG, "Sending ACTION_MEDIA_SHARED"); 573 intent = new Intent(Intent.ACTION_MEDIA_SHARED, 574 Uri.parse("file://" + path)); 575 mContext.sendBroadcast(intent); 576 } 577 578 /** 579 * Broadcasts the media bad removal event to all clients. 580 */ 581 void notifyMediaBadRemoval(String path) { 582 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL); 583 584 updateUsbMassStorageNotification(true, false); 585 setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title, 586 com.android.internal.R.string.ext_media_badremoval_notification_message, 587 com.android.internal.R.drawable.stat_sys_warning, 588 true, true, null); 589 590 handlePossibleExplicitUnmountBroadcast(path); 591 // Log.d(TAG, "Sending ACTION_MEDIA_BAD_REMOVAL"); 592 Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, 593 Uri.parse("file://" + path)); 594 mContext.sendBroadcast(intent); 595 } 596 597 /** 598 * Broadcasts the media unmountable event to all clients. 599 */ 600 void notifyMediaUnmountable(String path) { 601 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE); 602 603 Intent intent = new Intent(); 604 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); 605 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 606 607 setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title, 608 com.android.internal.R.string.ext_media_unmountable_notification_message, 609 com.android.internal.R.drawable.stat_notify_sdcard_usb, 610 true, false, pi); 611 updateUsbMassStorageNotification(false, false); 612 613 handlePossibleExplicitUnmountBroadcast(path); 614 615 // Log.d(TAG, "Sending ACTION_MEDIA_UNMOUNTABLE"); 616 intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, 617 Uri.parse("file://" + path)); 618 mContext.sendBroadcast(intent); 619 } 620 621 /** 622 * Broadcasts the media eject event to all clients. 623 */ 624 void notifyMediaUnmounting(String path) { 625 // Log.d(TAG, "Sending ACTION_MEDIA_EJECT"); 626 Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT, 627 Uri.parse("file://" + path)); 628 mContext.sendBroadcast(intent); 629 } 630 631 /** 632 * Sets the USB storage notification. 633 */ 634 private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible, 635 PendingIntent pi) { 636 637 if (!visible && mUsbStorageNotification == null) { 638 return; 639 } 640 641 NotificationManager notificationManager = (NotificationManager) mContext 642 .getSystemService(Context.NOTIFICATION_SERVICE); 643 644 if (notificationManager == null) { 645 return; 646 } 647 648 if (visible) { 649 Resources r = Resources.getSystem(); 650 CharSequence title = r.getText(titleId); 651 CharSequence message = r.getText(messageId); 652 653 if (mUsbStorageNotification == null) { 654 mUsbStorageNotification = new Notification(); 655 mUsbStorageNotification.icon = icon; 656 mUsbStorageNotification.when = 0; 657 } 658 659 if (sound && mPlaySounds) { 660 mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; 661 } else { 662 mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; 663 } 664 665 mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; 666 667 mUsbStorageNotification.tickerText = title; 668 if (pi == null) { 669 Intent intent = new Intent(); 670 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); 671 } 672 673 mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi); 674 } 675 676 final int notificationId = mUsbStorageNotification.icon; 677 if (visible) { 678 notificationManager.notify(notificationId, mUsbStorageNotification); 679 } else { 680 notificationManager.cancel(notificationId); 681 } 682 } 683 684 private synchronized boolean getMediaStorageNotificationDismissable() { 685 if ((mMediaStorageNotification != null) && 686 ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) == 687 Notification.FLAG_AUTO_CANCEL)) 688 return true; 689 690 return false; 691 } 692 693 /** 694 * Sets the media storage notification. 695 */ 696 private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible, 697 boolean dismissable, PendingIntent pi) { 698 699 if (!visible && mMediaStorageNotification == null) { 700 return; 701 } 702 703 NotificationManager notificationManager = (NotificationManager) mContext 704 .getSystemService(Context.NOTIFICATION_SERVICE); 705 706 if (notificationManager == null) { 707 return; 708 } 709 710 if (mMediaStorageNotification != null && visible) { 711 /* 712 * Dismiss the previous notification - we're about to 713 * re-use it. 714 */ 715 final int notificationId = mMediaStorageNotification.icon; 716 notificationManager.cancel(notificationId); 717 } 718 719 if (visible) { 720 Resources r = Resources.getSystem(); 721 CharSequence title = r.getText(titleId); 722 CharSequence message = r.getText(messageId); 723 724 if (mMediaStorageNotification == null) { 725 mMediaStorageNotification = new Notification(); 726 mMediaStorageNotification.when = 0; 727 } 728 729 if (mPlaySounds) { 730 mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND; 731 } else { 732 mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; 733 } 734 735 if (dismissable) { 736 mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; 737 } else { 738 mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; 739 } 740 741 mMediaStorageNotification.tickerText = title; 742 if (pi == null) { 743 Intent intent = new Intent(); 744 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); 745 } 746 747 mMediaStorageNotification.icon = icon; 748 mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi); 749 } 750 751 final int notificationId = mMediaStorageNotification.icon; 752 if (visible) { 753 notificationManager.notify(notificationId, mMediaStorageNotification); 754 } else { 755 notificationManager.cancel(notificationId); 756 } 757 } 758} 759 760