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