auto import from //depot/cupcake/@135843
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
new file mode 100644
index 0000000..8814e48
--- /dev/null
+++ b/services/java/com/android/server/MountService.java
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.IMountService;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.UEventObserver;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileReader;
+
+/**
+ * MountService implements an to the mount service daemon
+ * @hide
+ */
+class MountService extends IMountService.Stub {
+    
+    private static final String TAG = "MountService";
+
+    /**
+     * Binder context for this service
+     */
+    private Context mContext;
+    
+    /**
+     * listener object for communicating with the mount service daemon
+     */
+    private MountListener mListener;
+
+    /**
+     * The notification that is shown when a USB mass storage host
+     * is connected. 
+     * <p>
+     * This is lazily created, so use {@link #setUsbStorageNotification()}.
+     */
+    private Notification mUsbStorageNotification;
+
+
+    /**
+     * The notification that is shown when the following media events occur:
+     *     - Media is being checked
+     *     - Media is blank (or unknown filesystem)
+     *     - Media is corrupt
+     *     - Media is safe to unmount
+     *     - Media is missing
+     * <p>
+     * This is lazily created, so use {@link #setMediaStorageNotification()}.
+     */
+    private Notification mMediaStorageNotification;
+    
+    private boolean mShowSafeUnmountNotificationWhenUnmounted;
+
+    private boolean mPlaySounds;
+
+    private boolean mMounted;
+
+    /**
+     * Constructs a new MountService instance
+     * 
+     * @param context  Binder context for this service
+     */
+    public MountService(Context context) {
+        mContext = context;
+
+        // Register a BOOT_COMPLETED handler so that we can start
+        // MountListener. We defer the startup so that we don't
+        // start processing events before we ought-to
+        mContext.registerReceiver(mBroadcastReceiver,
+                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
+
+        mListener =  new MountListener(this);       
+        mShowSafeUnmountNotificationWhenUnmounted = false;
+
+        mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
+    }
+
+    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
+                Thread thread = new Thread(mListener, MountListener.class.getName());
+                thread.start();
+            }
+        }
+    };
+
+    /**
+     * @return true if USB mass storage support is enabled.
+     */
+    public boolean getMassStorageEnabled() throws RemoteException {
+        return mListener.getMassStorageEnabled();
+    }
+
+    /**
+     * Enables or disables USB mass storage support.
+     * 
+     * @param enable  true to enable USB mass storage support
+     */
+    public void setMassStorageEnabled(boolean enable) throws RemoteException {
+        mListener.setMassStorageEnabled(enable);
+    }
+
+    /**
+     * @return true if USB mass storage is connected.
+     */
+    public boolean getMassStorageConnected() throws RemoteException {
+        return mListener.getMassStorageConnected();
+    }
+    
+    /**
+     * Attempt to mount external media
+     */
+    public void mountMedia(String mountPath) throws RemoteException {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
+        }
+        mListener.mountMedia(mountPath);
+    }
+
+    /**
+     * Attempt to unmount external media to prepare for eject
+     */
+    public void unmountMedia(String mountPath) throws RemoteException {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
+        }
+
+        // Set a flag so that when we get the unmounted event, we know
+        // to display the notification
+        mShowSafeUnmountNotificationWhenUnmounted = true;
+
+        // tell mountd to unmount the media
+        mListener.ejectMedia(mountPath);
+    }
+
+    /**
+     * Attempt to format external media
+     */
+    public void formatMedia(String formatPath) throws RemoteException {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS) 
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
+        }
+
+        mListener.formatMedia(formatPath);
+    }
+
+    /**
+     * Returns true if we're playing media notification sounds.
+     */
+    public boolean getPlayNotificationSounds() {
+        return mPlaySounds;
+    }
+
+    /**
+     * Set whether or not we're playing media notification sounds.
+     */
+    public void setPlayNotificationSounds(boolean enabled) {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.WRITE_SETTINGS) 
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires WRITE_SETTINGS permission");
+        }
+        mPlaySounds = enabled;
+        SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0"));
+    }
+
+    /**
+     * Update the state of the USB mass storage notification
+     */
+    void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) {
+
+        try {
+
+            if (getMassStorageConnected() && !suppressIfConnected) {
+                Intent intent = new Intent();
+                intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
+                PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+                setUsbStorageNotification(
+                        com.android.internal.R.string.usb_storage_notification_title,
+                        com.android.internal.R.string.usb_storage_notification_message,
+                        com.android.internal.R.drawable.stat_sys_data_usb,
+                        sound, true, pi);
+            } else {
+                setUsbStorageNotification(0, 0, 0, false, false, null);
+            }
+        } catch (RemoteException e) {
+            // Nothing to do
+        }
+    }
+
+    void handlePossibleExplicitUnmountBroadcast(String path) {
+        if (mMounted) {
+            mMounted = false;
+            Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 
+                    Uri.parse("file://" + path));
+            mContext.sendBroadcast(intent);
+        }
+    }
+
+    /**
+     * Broadcasts the USB mass storage connected event to all clients.
+     */
+    void notifyUmsConnected() {
+        String storageState = Environment.getExternalStorageState();
+        if (!storageState.equals(Environment.MEDIA_REMOVED) &&
+            !storageState.equals(Environment.MEDIA_BAD_REMOVAL) &&
+            !storageState.equals(Environment.MEDIA_CHECKING)) {
+
+            updateUsbMassStorageNotification(false, true);
+        }
+
+        Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Broadcasts the USB mass storage disconnected event to all clients.
+     */
+    void notifyUmsDisconnected() {
+        updateUsbMassStorageNotification(false, false);
+        Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Broadcasts the media removed event to all clients.
+     */
+    void notifyMediaRemoved(String path) {
+        updateUsbMassStorageNotification(true, false);
+
+        setMediaStorageNotification(
+                com.android.internal.R.string.ext_media_nomedia_notification_title,
+                com.android.internal.R.string.ext_media_nomedia_notification_message,
+                com.android.internal.R.drawable.stat_sys_no_sim,
+                true, false, null);
+        handlePossibleExplicitUnmountBroadcast(path);
+
+        Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED, 
+                Uri.parse("file://" + path));
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Broadcasts the media unmounted event to all clients.
+     */
+    void notifyMediaUnmounted(String path) {
+        if (mShowSafeUnmountNotificationWhenUnmounted) {
+            setMediaStorageNotification(
+                    com.android.internal.R.string.ext_media_safe_unmount_notification_title,
+                    com.android.internal.R.string.ext_media_safe_unmount_notification_message,
+                    com.android.internal.R.drawable.stat_notify_sim_toolkit,
+                    true, true, null);
+            mShowSafeUnmountNotificationWhenUnmounted = false;
+        } else {
+            setMediaStorageNotification(0, 0, 0, false, false, null);
+        }
+        updateUsbMassStorageNotification(false, false);
+
+        Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 
+                Uri.parse("file://" + path));
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Broadcasts the media checking event to all clients.
+     */
+    void notifyMediaChecking(String path) {
+        setMediaStorageNotification(
+                com.android.internal.R.string.ext_media_checking_notification_title,
+                com.android.internal.R.string.ext_media_checking_notification_message,
+                com.android.internal.R.drawable.stat_notify_sim_toolkit,
+                true, false, null);
+
+        updateUsbMassStorageNotification(true, false);
+        Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING, 
+                Uri.parse("file://" + path));
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Broadcasts the media nofs event to all clients.
+     */
+    void notifyMediaNoFs(String path) {
+        
+        Intent intent = new Intent();
+        intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
+        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+        setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title,
+                                    com.android.internal.R.string.ext_media_nofs_notification_message,
+                                    com.android.internal.R.drawable.stat_sys_no_sim,
+                                    true, false, pi);
+        updateUsbMassStorageNotification(false, false);
+        intent = new Intent(Intent.ACTION_MEDIA_NOFS, 
+                Uri.parse("file://" + path));
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Broadcasts the media mounted event to all clients.
+     */
+    void notifyMediaMounted(String path, boolean readOnly) {
+        setMediaStorageNotification(0, 0, 0, false, false, null);
+        updateUsbMassStorageNotification(false, false);
+        Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, 
+                Uri.parse("file://" + path));
+        intent.putExtra("read-only", readOnly);
+        mMounted = true;
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Broadcasts the media shared event to all clients.
+     */
+    void notifyMediaShared(String path) {
+        Intent intent = new Intent();
+        intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
+        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+        setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
+                                  com.android.internal.R.string.usb_storage_stop_notification_message,
+                                  com.android.internal.R.drawable.stat_sys_warning,
+                                  false, true, pi);
+        handlePossibleExplicitUnmountBroadcast(path);
+        intent = new Intent(Intent.ACTION_MEDIA_SHARED, 
+                Uri.parse("file://" + path));
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Broadcasts the media bad removal event to all clients.
+     */
+    void notifyMediaBadRemoval(String path) {
+        updateUsbMassStorageNotification(true, false);
+        setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title,
+                                    com.android.internal.R.string.ext_media_badremoval_notification_message,
+                                    com.android.internal.R.drawable.stat_sys_warning,
+                                    true, true, null);
+
+        handlePossibleExplicitUnmountBroadcast(path);
+        Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, 
+                Uri.parse("file://" + path));
+        mContext.sendBroadcast(intent);
+
+        intent = new Intent(Intent.ACTION_MEDIA_REMOVED, 
+                Uri.parse("file://" + path));
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Broadcasts the media unmountable event to all clients.
+     */
+    void notifyMediaUnmountable(String path) {
+        Intent intent = new Intent();
+        intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
+        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+        setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title,
+                                    com.android.internal.R.string.ext_media_unmountable_notification_message,
+                                    com.android.internal.R.drawable.stat_sys_no_sim,
+                                    true, false, pi); 
+        updateUsbMassStorageNotification(false, false);
+
+        handlePossibleExplicitUnmountBroadcast(path);
+
+        intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, 
+                Uri.parse("file://" + path));
+        mContext.sendBroadcast(intent);
+    }
+    
+    /**
+     * Broadcasts the media eject event to all clients.
+     */
+    void notifyMediaEject(String path) {
+        Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT, 
+                Uri.parse("file://" + path));
+        mContext.sendBroadcast(intent);
+    }
+    
+    /**
+     * Sets the USB storage notification.
+     */
+    private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible,
+                                                        PendingIntent pi) {
+
+        if (!visible && mUsbStorageNotification == null) {
+            return;
+        }
+
+        NotificationManager notificationManager = (NotificationManager) mContext
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+
+        if (notificationManager == null) {
+            return;
+        }
+        
+        if (visible) {
+            Resources r = Resources.getSystem();
+            CharSequence title = r.getText(titleId);
+            CharSequence message = r.getText(messageId);
+
+            if (mUsbStorageNotification == null) {
+                mUsbStorageNotification = new Notification();
+                mUsbStorageNotification.icon = icon;
+                mUsbStorageNotification.when = 0;
+            }
+
+            if (sound && mPlaySounds) {
+                mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
+            } else {
+                mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
+            }
+                
+            mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
+
+            mUsbStorageNotification.tickerText = title;
+            if (pi == null) {
+                Intent intent = new Intent();
+                pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+            }
+
+            mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
+        }
+    
+        final int notificationId = mUsbStorageNotification.icon;
+        if (visible) {
+            notificationManager.notify(notificationId, mUsbStorageNotification);
+        } else {
+            notificationManager.cancel(notificationId);
+        }
+    }
+
+    private synchronized boolean getMediaStorageNotificationDismissable() {
+        if ((mMediaStorageNotification != null) &&
+            ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
+                    Notification.FLAG_AUTO_CANCEL))
+            return true;
+
+        return false;
+    }
+
+    /**
+     * Sets the media storage notification.
+     */
+    private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
+                                                          boolean dismissable, PendingIntent pi) {
+
+        if (!visible && mMediaStorageNotification == null) {
+            return;
+        }
+
+        NotificationManager notificationManager = (NotificationManager) mContext
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+
+        if (notificationManager == null) {
+            return;
+        }
+
+        if (mMediaStorageNotification != null && visible) {
+            /*
+             * Dismiss the previous notification - we're about to
+             * re-use it.
+             */
+            final int notificationId = mMediaStorageNotification.icon;
+            notificationManager.cancel(notificationId);
+        }
+        
+        if (visible) {
+            Resources r = Resources.getSystem();
+            CharSequence title = r.getText(titleId);
+            CharSequence message = r.getText(messageId);
+
+            if (mMediaStorageNotification == null) {
+                mMediaStorageNotification = new Notification();
+                mMediaStorageNotification.when = 0;
+            }
+
+            if (mPlaySounds) {
+                mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND;
+            } else {
+                mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
+            }
+
+            if (dismissable) {
+                mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
+            } else {
+                mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
+            }
+
+            mMediaStorageNotification.tickerText = title;
+            if (pi == null) {
+                Intent intent = new Intent();
+                pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+            }
+
+            mMediaStorageNotification.icon = icon;
+            mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
+        }
+    
+        final int notificationId = mMediaStorageNotification.icon;
+        if (visible) {
+            notificationManager.notify(notificationId, mMediaStorageNotification);
+        } else {
+            notificationManager.cancel(notificationId);
+        }
+    }
+}
+