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(); } }