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