diff --git a/api/current.xml b/api/current.xml
index 492bb347a5fb3..394a68a10872f 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -120110,7 +120110,7 @@
visibility="public"
>
Select to copy files to/from your computer.
-
Turn off USB storage
@@ -2084,6 +2083,15 @@
There was a problem turning off USB storage. Check to make sure you have unmounted the USB host, then try again.
+
+ Enable Mass Storage
+
+ Some processes accessing data on sdcard will be killed. Do you want to continue?
+
+ UMS operation failed
+
+ OK
+
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 4485c79ee6a3a..41f38501bd3f0 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -111,15 +111,18 @@ class MountService extends IMountService.Stub
private String mLegacyState = Environment.MEDIA_REMOVED;
private PackageManagerService mPms;
private boolean mUmsEnabling;
- private ArrayList mListeners;
+ // Used as a lock for methods that register/unregister listeners.
+ final private ArrayList mListeners =
+ new ArrayList();
private boolean mBooted = false;
private boolean mReady = false;
private boolean mSendUmsConnectedOnBoot = false;
/**
* Private hash of currently mounted secure containers.
+ * Used as a lock in methods to manipulate secure containers.
*/
- private HashSet mAsecMountSet = new HashSet();
+ final private HashSet mAsecMountSet = new HashSet();
private static final int H_UNMOUNT_PM_UPDATE = 1;
private static final int H_UNMOUNT_PM_DONE = 2;
@@ -148,6 +151,25 @@ class MountService extends IMountService.Stub
this.path = path;
this.force = force;
}
+
+ void handleFinished() {
+ doUnmountVolume(path, true);
+ }
+ }
+
+ class UmsEnableCallBack extends UnmountCallBack {
+ String method;
+
+ UmsEnableCallBack(String path, String method, boolean force) {
+ super(path, force);
+ this.method = method;
+ }
+
+ @Override
+ void handleFinished() {
+ super.handleFinished();
+ doShareUnshareVolume(path, method, true);
+ }
}
final private Handler mHandler = new Handler() {
@@ -217,8 +239,7 @@ class MountService extends IMountService.Stub
}
case H_UNMOUNT_MS : {
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
- String path = ucb.path;
- doUnmountVolume(path, true);
+ ucb.handleFinished();
break;
}
}
@@ -298,52 +319,18 @@ class MountService extends IMountService.Stub
}
}
- private int doShareUnshareVolume(String path, String method, boolean enable) {
- validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
-
+ private void doShareUnshareVolume(String path, String method, boolean enable) {
// TODO: Add support for multiple share methods
if (!method.equals("ums")) {
throw new IllegalArgumentException(String.format("Method %s not supported", method));
}
- /*
- * If the volume is mounted and we're enabling then unmount it
- */
- String vs = getVolumeState(path);
- if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
- mUmsEnabling = enable; // Override for isUsbMassStorageEnabled()
- int rc = doUnmountVolume(path, true);
- mUmsEnabling = false; // Clear override
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, String.format("Failed to unmount before enabling UMS (%d)", rc));
- return rc;
- }
- }
-
try {
mConnector.doCommand(String.format(
"volume %sshare %s %s", (enable ? "" : "un"), path, method));
} catch (NativeDaemonConnectorException e) {
Log.e(TAG, "Failed to share/unshare", e);
- return StorageResultCode.OperationFailedInternalError;
}
-
- /*
- * If we disabled UMS then mount the volume
- */
- if (!enable) {
- if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, String.format(
- "Failed to remount %s after disabling share method %s", path, method));
- /*
- * Even though the mount failed, the unshare didn't so don't indicate an error.
- * The mountVolume() call will have set the storage state and sent the necessary
- * broadcasts.
- */
- }
- }
-
- return StorageResultCode.OperationSucceeded;
}
private void updatePublicVolumeState(String path, String state) {
@@ -547,7 +534,7 @@ class MountService extends IMountService.Stub
if (!vs.equals(
Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
Environment.MEDIA_NOFS) && !vs.equals(
- Environment.MEDIA_UNMOUNTABLE) && !mUmsEnabling) {
+ Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
}
@@ -791,8 +778,6 @@ class MountService extends IMountService.Stub
mContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
- mListeners = new ArrayList();
-
/*
* Vold does not run in the simulator, so pretend the connector thread
* ran and did its thing.
@@ -852,9 +837,7 @@ class MountService extends IMountService.Stub
* the UMS host could have dirty FAT cache entries
* yet to flush.
*/
- if (setUsbMassStorageEnabled(false) != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, "UMS disable on shutdown failed");
- }
+ setUsbMassStorageEnabled(false);
} else if (state.equals(Environment.MEDIA_CHECKING)) {
/*
* If the media is being checked, then we need to wait for
@@ -886,19 +869,62 @@ class MountService extends IMountService.Stub
}
}
+ private boolean getUmsEnabling() {
+ synchronized (mListeners) {
+ return mUmsEnabling;
+ }
+ }
+
+ private void setUmsEnabling(boolean enable) {
+ synchronized (mListeners) {
+ mUmsEnabling = true;
+ }
+ }
+
public boolean isUsbMassStorageConnected() {
waitForReady();
- if (mUmsEnabling) {
+ if (getUmsEnabling()) {
return true;
}
return doGetShareMethodAvailable("ums");
}
- public int setUsbMassStorageEnabled(boolean enable) {
+ public void setUsbMassStorageEnabled(boolean enable) {
waitForReady();
+ validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
- return doShareUnshareVolume(Environment.getExternalStorageDirectory().getPath(), "ums", enable);
+ // TODO: Add support for multiple share methods
+
+ /*
+ * If the volume is mounted and we're enabling then unmount it
+ */
+ String path = Environment.getExternalStorageDirectory().getPath();
+ String vs = getVolumeState(path);
+ String method = "ums";
+ if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
+ // Override for isUsbMassStorageEnabled()
+ setUmsEnabling(enable);
+ UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
+ mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
+ // Clear override
+ setUmsEnabling(false);
+ }
+ /*
+ * If we disabled UMS then mount the volume
+ */
+ if (!enable) {
+ doShareUnshareVolume(path, method, enable);
+ if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
+ Log.e(TAG, "Failed to remount " + path +
+ " after disabling share method " + method);
+ /*
+ * Even though the mount failed, the unshare didn't so don't indicate an error.
+ * The mountVolume() call will have set the storage state and sent the necessary
+ * broadcasts.
+ */
+ }
+ }
}
public boolean isUsbMassStorageEnabled() {
diff --git a/services/java/com/android/server/status/UsbStorageActivity.java b/services/java/com/android/server/status/UsbStorageActivity.java
index 7a2a2d67d1e51..c1c8c22230d7f 100644
--- a/services/java/com/android/server/status/UsbStorageActivity.java
+++ b/services/java/com/android/server/status/UsbStorageActivity.java
@@ -16,25 +16,27 @@
package com.android.server.status;
+import com.android.internal.R;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.DialogInterface.OnCancelListener;
import android.os.Bundle;
-import android.os.Handler;
import android.os.Environment;
+import android.os.IBinder;
+import android.os.storage.IMountService;
import android.os.storage.StorageManager;
import android.os.storage.StorageEventListener;
-import android.os.storage.StorageResultCode;
-import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.widget.ImageView;
import android.widget.Button;
import android.widget.TextView;
-import android.widget.Toast;
import android.view.View;
import android.util.Log;
@@ -43,7 +45,8 @@ import android.util.Log;
* on-demand (that is, when the USB cable is connected). It uses the alert
* dialog style. It will be launched from a notification.
*/
-public class UsbStorageActivity extends Activity {
+public class UsbStorageActivity extends Activity
+ implements View.OnClickListener, OnCancelListener {
private static final String TAG = "UsbStorageActivity";
private Button mMountButton;
private Button mUnmountButton;
@@ -51,6 +54,9 @@ public class UsbStorageActivity extends Activity {
private TextView mMessage;
private ImageView mIcon;
private StorageManager mStorageManager = null;
+ private static final int DLG_CONFIRM_KILL_STORAGE_USERS = 1;
+ private static final int DLG_ERROR_SHARING = 2;
+ static final boolean localLOGV = false;
/** Used to detect when the USB cable is unplugged, so we can call finish() */
private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
@@ -82,7 +88,6 @@ public class UsbStorageActivity extends Activity {
if (mStorageManager == null) {
Log.w(TAG, "Failed to get StorageManager");
}
- mStorageManager.registerListener(mStorageListener);
}
setTitle(getString(com.android.internal.R.string.usb_storage_activity_title));
@@ -94,28 +99,9 @@ public class UsbStorageActivity extends Activity {
mMessage = (TextView) findViewById(com.android.internal.R.id.message);
mMountButton = (Button) findViewById(com.android.internal.R.id.mount_button);
- mMountButton.setOnClickListener(
- new View.OnClickListener() {
- public void onClick(View v) {
- int rc = mStorageManager.enableUsbMassStorage();
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, String.format("UMS enable failed (%d)", rc));
- showSharingError();
- }
- }
- });
-
+ mMountButton.setOnClickListener(this);
mUnmountButton = (Button) findViewById(com.android.internal.R.id.unmount_button);
- mUnmountButton.setOnClickListener(
- new View.OnClickListener() {
- public void onClick(View v) {
- int rc = mStorageManager.disableUsbMassStorage();
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, String.format("UMS disable failed (%d)", rc));
- showStoppingError();
- }
- }
- });
+ mUnmountButton.setOnClickListener(this);
}
private void switchDisplay(boolean usbStorageInUse) {
@@ -138,6 +124,7 @@ public class UsbStorageActivity extends Activity {
protected void onResume() {
super.onResume();
+ mStorageManager.registerListener(mStorageListener);
registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
try {
switchDisplay(mStorageManager.isUsbMassStorageEnabled());
@@ -151,6 +138,9 @@ public class UsbStorageActivity extends Activity {
super.onPause();
unregisterReceiver(mBatteryReceiver);
+ if (mStorageManager == null && mStorageListener != null) {
+ mStorageManager.unregisterListener(mStorageListener);
+ }
}
private void handleBatteryChanged(Intent intent) {
@@ -160,15 +150,81 @@ public class UsbStorageActivity extends Activity {
finish();
}
}
-
- private void showSharingError() {
- Toast.makeText(this, com.android.internal.R.string.usb_storage_error_message,
- Toast.LENGTH_LONG).show();
+
+ private IMountService getMountService() {
+ IBinder service = ServiceManager.getService("mount");
+ if (service != null) {
+ return IMountService.Stub.asInterface(service);
+ }
+ return null;
}
-
- private void showStoppingError() {
- Toast.makeText(this, com.android.internal.R.string.usb_storage_stop_error_message,
- Toast.LENGTH_LONG).show();
+
+ @Override
+ public Dialog onCreateDialog(int id, Bundle args) {
+ switch (id) {
+ case DLG_CONFIRM_KILL_STORAGE_USERS:
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.dlg_confirm_kill_storage_users_title)
+ .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ mStorageManager.enableUsbMassStorage();
+ }})
+ .setNegativeButton(R.string.cancel, null)
+ .setMessage(R.string.dlg_confirm_kill_storage_users_text)
+ .setOnCancelListener(this)
+ .create();
+ case DLG_ERROR_SHARING:
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.dlg_error_title)
+ .setNeutralButton(R.string.dlg_ok, null)
+ .setMessage(R.string.usb_storage_error_message)
+ .setOnCancelListener(this)
+ .create();
+ }
+ return null;
+ }
+
+ private void showDialogInner(int id) {
+ removeDialog(id);
+ showDialog(id);
+ }
+
+ private void checkStorageUsers() {
+ IMountService ims = getMountService();
+ if (ims == null) {
+ // Display error dialog
+ showDialogInner(DLG_ERROR_SHARING);
+ }
+ String path = Environment.getExternalStorageDirectory().getPath();
+ int stUsers[] = null;
+ try {
+ if (localLOGV) Log.i(TAG, "Checking getStorageUsers");
+ stUsers = ims.getStorageUsers(path);
+ } catch (RemoteException e) {
+ showDialogInner(DLG_ERROR_SHARING);
+ }
+ if (stUsers != null && stUsers.length > 0) {
+ // Display dialog to user
+ showDialogInner(DLG_CONFIRM_KILL_STORAGE_USERS);
+ } else {
+ if (localLOGV) Log.i(TAG, "Enabling UMS");
+ mStorageManager.enableUsbMassStorage();
+ }
+ }
+
+ public void onClick(View v) {
+ Log.i(TAG, "Clicked button");
+ if (v == mMountButton) {
+ // Check for list of storage users and display dialog if needed.
+ checkStorageUsers();
+ } else if (v == mUnmountButton) {
+ if (localLOGV) Log.i(TAG, "Disabling UMS");
+ mStorageManager.disableUsbMassStorage();
+ }
+ }
+
+ public void onCancel(DialogInterface dialog) {
+ finish();
}
}