Merge "MountService: Add new Settings for UMS prompting and notification behavior"
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl
index 2f60c34..bf6b698 100644
--- a/core/java/android/os/IMountService.aidl
+++ b/core/java/android/os/IMountService.aidl
@@ -65,18 +65,6 @@
     void setPlayNotificationSounds(boolean value);
 
     /**
-     * Returns true if USB Mass Storage is automatically started
-     * when a UMS host is detected.
-     */
-    boolean getAutoStartUms();
-
-    /**
-     * Sets whether or not USB Mass Storage is automatically started
-     * when a UMS host is detected.
-     */
-    void setAutoStartUms(boolean value);
-
-    /**
      * Gets the state of an volume via it's mountpoint.
      */
     String getVolumeState(String mountPoint);
@@ -120,5 +108,4 @@
      * all external media.
      */
     void shutdown();
-
 }
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 473cc7a..bcdf5e8 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -30,9 +30,14 @@
 import android.os.Environment;
 import android.os.SystemProperties;
 import android.os.UEventObserver;
+import android.os.Handler;
 import android.text.TextUtils;
 import android.util.Log;
 
+import android.provider.Settings;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+
 import java.io.File;
 import java.io.FileReader;
 import java.lang.IllegalStateException;
@@ -95,7 +100,10 @@
 
     private boolean mMounted;
 
+    private SettingsWatcher mSettingsWatcher;
     private boolean mAutoStartUms;
+    private boolean mPromptUms;
+    private boolean mUmsActiveNotify;
 
     private boolean mUmsConnected = false;
     private boolean mUmsEnabled = false;
@@ -121,7 +129,76 @@
 
         mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
 
-        mAutoStartUms = SystemProperties.get("persist.service.mount.umsauto", "0").equals("1");
+        ContentResolver cr = mContext.getContentResolver();
+        mAutoStartUms = (Settings.Secure.getInt(
+                cr, Settings.Secure.MOUNT_UMS_AUTOSTART, 0) == 1);
+        mPromptUms = (Settings.Secure.getInt(
+                cr, Settings.Secure.MOUNT_UMS_PROMPT, 1) == 1);
+        mUmsActiveNotify = (Settings.Secure.getInt(
+                cr, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, 1) == 1);
+
+        mSettingsWatcher = new SettingsWatcher(new Handler());
+    }
+  
+    private class SettingsWatcher extends ContentObserver {
+        public SettingsWatcher(Handler handler) {
+            super(handler);
+            ContentResolver cr = mContext.getContentResolver();
+            cr.registerContentObserver(Settings.System.getUriFor(
+                    Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND), false, this);
+            cr.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.MOUNT_UMS_AUTOSTART), false, this);
+            cr.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.MOUNT_UMS_PROMPT), false, this);
+            cr.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED), false, this);
+        }
+
+        public void onChange(boolean selfChange) {
+            super.onChange(selfChange);
+            ContentResolver cr = mContext.getContentResolver();
+
+            boolean newPlayNotificationSounds = (Settings.Secure.getInt(
+                    cr, Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND, 1) == 1);
+
+            boolean newUmsAutostart = (Settings.Secure.getInt(
+                    cr, Settings.Secure.MOUNT_UMS_AUTOSTART, 0) == 1);
+
+            if (newUmsAutostart != mAutoStartUms) {
+                Log.d(TAG, "Changing UMS autostart to " + newUmsAutostart);
+                mAutoStartUms = newUmsAutostart;
+            }
+
+            boolean newUmsPrompt = (Settings.Secure.getInt(
+                    cr, Settings.Secure.MOUNT_UMS_PROMPT, 1) == 1);
+
+            if (newUmsPrompt != mPromptUms) {
+                Log.d(TAG, "Changing UMS prompt to " + newUmsPrompt);
+                mPromptUms = newUmsAutostart;
+            }
+
+            boolean newUmsNotifyEnabled = (Settings.Secure.getInt(
+                    cr, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, 1) == 1);
+
+            Log.d(TAG, "new notify enabled = " + newUmsNotifyEnabled);
+            if (mUmsEnabled) {
+                if (newUmsNotifyEnabled) {
+                    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);
+                } else {
+                    setUsbStorageNotification(0, 0, 0, false, false, null);
+                }
+            }
+            if (newUmsNotifyEnabled != mUmsActiveNotify) {
+                Log.d(TAG, "Changing UMS active notification to " + newUmsNotifyEnabled);
+                mUmsActiveNotify = newUmsNotifyEnabled;
+            }
+        }
     }
 
     BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -205,13 +282,18 @@
      * @param enable  true to enable USB mass storage support
      */
     public void setMassStorageEnabled(boolean enable) throws IllegalStateException {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
+        }
         try {
             String vp = Environment.getExternalStorageDirectory().getPath();
             String vs = getVolumeState(vp);
 
             if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
-                Log.d(TAG, "Unmounting media before UMS enable");
-                unmountMedia(vp);
+                mListener.unmountVolume(vp);
+                updateUsbMassStorageNotification(true, false);
             }
 
             mListener.setShareMethodEnabled(Environment
@@ -220,8 +302,12 @@
                                             "ums", enable);
             mUmsEnabled = enable;
             if (!enable) {
-                Log.d(TAG, "Mounting media after UMS disable");
                 mountMedia(vp);
+                if (mPromptUms) {
+                    updateUsbMassStorageNotification(false, false);
+                } else {
+                    updateUsbMassStorageNotification(true, false);
+                }
             }
         } catch (IllegalStateException rex) {
             Log.e(TAG, "Failed to set ums enable {" + enable + "}");
@@ -316,26 +402,6 @@
         SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0"));
     }
 
-    /**
-     * Returns true if we auto-start UMS on cable insertion.
-     */
-    public boolean getAutoStartUms() {
-        return mAutoStartUms;
-    }
-
-    /**
-     * Set whether or not we're playing media notification sounds.
-     */
-    public void setAutoStartUms(boolean enabled) {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.WRITE_SETTINGS) 
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires WRITE_SETTINGS permission");
-        }
-        mAutoStartUms = enabled;
-        SystemProperties.set("persist.service.mount.umsauto", (enabled ? "1" : "0"));
-    }
-
     void updatePublicVolumeState(String mountPoint, String state) {
         if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
             Log.w(TAG, "Multiple volumes not currently supported");
@@ -395,7 +461,7 @@
                         Log.d(TAG, "Skipping connection-mount; already mounted");
                     }
                 } catch (IllegalStateException rex) {
-                    Log.e(TAG, "Exception while handling connection mount " + rex);
+                    Log.e(TAG, "Exception while handling connection mount ", rex);
                 }
 
                 try {
@@ -456,7 +522,7 @@
                     setMassStorageEnabled(true);
                 } catch (IllegalStateException e) {
                 }
-            } else {
+            } else if (mPromptUms) {
                 updateUsbMassStorageNotification(false, true);
             }
         }
@@ -465,17 +531,29 @@
         mContext.sendBroadcast(intent);
     }
 
-    void notifyShareAvailabilityChange(String method, boolean avail) {
-        Log.d(TAG, "Share method {" + method + "} availability now " + avail);
+    void notifyShareAvailabilityChange(String method, final boolean avail) {
         if (!method.equals("ums")) {
            Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
            return;
         }
-        if (avail) {
-            notifyUmsConnected();
-        } else {
-            notifyUmsDisconnected();
-        }
+
+        /*
+         * Notification needs to run in a different thread as
+         * it may need to call back into vold
+         */
+        new Thread() {
+            public void run() {
+                try {
+                    if (avail) {
+                        notifyUmsConnected();
+                    } else {
+                        notifyUmsDisconnected();
+                    }
+                } catch (Exception ex) {
+                    Log.w(TAG, "Failed to mount media on insertion");
+                }
+            }
+        }.start();
     }
 
     /**
@@ -483,6 +561,14 @@
      */
     void notifyUmsDisconnected() {
         mUmsConnected = false;
+        if (mUmsEnabled) {
+            try {
+                Log.w(TAG, "UMS disconnected while enabled!");
+                setMassStorageEnabled(false);
+            } catch (Exception ex) {
+                Log.e(TAG, "Error disabling UMS on unsafe UMS disconnect", ex);
+            }
+        }
         updateUsbMassStorageNotification(false, false);
         Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
         mContext.sendBroadcast(intent);
@@ -495,7 +581,7 @@
                     Log.d(TAG, "Mounting media after insertion");
                     mountMedia(path);
                 } catch (Exception ex) {
-                    Log.w(TAG, "Failed to mount media on insertion");
+                    Log.w(TAG, "Failed to mount media on insertion", ex);
                 }
             }
         }.start();
@@ -522,7 +608,6 @@
             true, false, null);
         handlePossibleExplicitUnmountBroadcast(path);
 
-        // Log.d(TAG, "Sending ACTION_MEDIA_REMOVED");
         Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED, 
                 Uri.parse("file://" + path));
         mContext.sendBroadcast(intent);
@@ -547,7 +632,6 @@
         }
         updateUsbMassStorageNotification(false, false);
 
-        // Log.d(TAG, "Sending ACTION_MEDIA_UNMOUNTED");
         Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 
                 Uri.parse("file://" + path));
         mContext.sendBroadcast(intent);
@@ -566,7 +650,6 @@
                 true, false, null);
 
         updateUsbMassStorageNotification(true, false);
-        // Log.d(TAG, "Sending ACTION_MEDIA_CHECKING");
         Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING, 
                 Uri.parse("file://" + path));
         mContext.sendBroadcast(intent);
@@ -587,7 +670,6 @@
                                     com.android.internal.R.drawable.stat_notify_sdcard_usb,
                                     true, false, pi);
         updateUsbMassStorageNotification(false, false);
-        // Log.d(TAG, "Sending ACTION_MEDIA_NOFS");
         intent = new Intent(Intent.ACTION_MEDIA_NOFS, 
                 Uri.parse("file://" + path));
         mContext.sendBroadcast(intent);
@@ -601,7 +683,6 @@
 
         setMediaStorageNotification(0, 0, 0, false, false, null);
         updateUsbMassStorageNotification(false, false);
-        // Log.d(TAG, "Sending ACTION_MEDIA_MOUNTED");
         Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, 
                 Uri.parse("file://" + path));
         intent.putExtra("read-only", readOnly);
@@ -620,16 +701,17 @@
 
         updatePublicVolumeState(path, Environment.MEDIA_SHARED);
 
-        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);
+        if (mUmsActiveNotify) {
+            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);
-        // Log.d(TAG, "Sending ACTION_MEDIA_SHARED");
-        intent = new Intent(Intent.ACTION_MEDIA_SHARED, 
+        Intent intent = new Intent(Intent.ACTION_MEDIA_SHARED,
                 Uri.parse("file://" + path));
         mContext.sendBroadcast(intent);
     }
@@ -647,7 +729,6 @@
                                     true, true, null);
 
         handlePossibleExplicitUnmountBroadcast(path);
-        // Log.d(TAG, "Sending ACTION_MEDIA_BAD_REMOVAL");
         Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, 
                 Uri.parse("file://" + path));
         mContext.sendBroadcast(intent);
@@ -671,7 +752,6 @@
 
         handlePossibleExplicitUnmountBroadcast(path);
 
-        // Log.d(TAG, "Sending ACTION_MEDIA_UNMOUNTABLE");
         intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, 
                 Uri.parse("file://" + path));
         mContext.sendBroadcast(intent);
@@ -681,7 +761,6 @@
      * Broadcasts the media eject event to all clients.
      */
     void notifyMediaUnmounting(String path) {
-        // Log.d(TAG, "Sending ACTION_MEDIA_EJECT");
         Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT, 
                 Uri.parse("file://" + path));
         mContext.sendBroadcast(intent);