DO NOT MERGE MountService: Add support for multiple volumes
Change-Id: I45ee0e5735a6d72c635f6d22320e8b13bccc3847 Signed-off-by: Mike Lockwood <lockwood@android.com>
This commit is contained in:
@ -144,7 +144,8 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
|
||||
|
||||
private Context mContext;
|
||||
private NativeDaemonConnector mConnector;
|
||||
private String mLegacyState = Environment.MEDIA_REMOVED;
|
||||
private final HashMap<String, String> mVolumeStates = new HashMap<String, String>();
|
||||
private String mExternalStoragePath;
|
||||
private PackageManagerService mPms;
|
||||
private boolean mUmsEnabling;
|
||||
// Used as a lock for methods that register/unregister listeners.
|
||||
@ -449,22 +450,25 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
String path = Environment.getExternalStorageDirectory().getPath();
|
||||
String state = getVolumeState(path);
|
||||
synchronized (mVolumeStates) {
|
||||
for (String path : mVolumeStates.keySet()) {
|
||||
String state = mVolumeStates.get(path);
|
||||
|
||||
if (mEmulateExternalStorage) {
|
||||
notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Mounted);
|
||||
} else if (state.equals(Environment.MEDIA_UNMOUNTED)) {
|
||||
int rc = doMountVolume(path);
|
||||
if (rc != StorageResultCode.OperationSucceeded) {
|
||||
Slog.e(TAG, String.format("Boot-time mount failed (%d)", rc));
|
||||
if (state.equals(Environment.MEDIA_UNMOUNTED)) {
|
||||
int rc = doMountVolume(path);
|
||||
if (rc != StorageResultCode.OperationSucceeded) {
|
||||
Slog.e(TAG, String.format("Boot-time mount failed (%d)",
|
||||
rc));
|
||||
}
|
||||
} else if (state.equals(Environment.MEDIA_SHARED)) {
|
||||
/*
|
||||
* Bootstrap UMS enabled state since vold indicates
|
||||
* the volume is shared (runtime restart while ums enabled)
|
||||
*/
|
||||
notifyVolumeStateChange(null, path, VolumeState.NoMedia,
|
||||
VolumeState.Shared);
|
||||
}
|
||||
}
|
||||
} else if (state.equals(Environment.MEDIA_SHARED)) {
|
||||
/*
|
||||
* Bootstrap UMS enabled state since vold indicates
|
||||
* the volume is shared (runtime restart while ums enabled)
|
||||
*/
|
||||
notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Shared);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -516,35 +520,36 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
|
||||
}
|
||||
|
||||
private void updatePublicVolumeState(String path, String state) {
|
||||
if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
|
||||
Slog.w(TAG, "Multiple volumes not currently supported");
|
||||
String oldState;
|
||||
synchronized(mVolumeStates) {
|
||||
oldState = mVolumeStates.put(path, state);
|
||||
}
|
||||
if (state.equals(oldState)) {
|
||||
Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",
|
||||
state, state, path));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mLegacyState.equals(state)) {
|
||||
Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
|
||||
return;
|
||||
}
|
||||
// Update state on PackageManager, but only of real events
|
||||
if (!mEmulateExternalStorage) {
|
||||
if (Environment.MEDIA_UNMOUNTED.equals(state)) {
|
||||
mPms.updateExternalMediaStatus(false, false);
|
||||
Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
|
||||
|
||||
/*
|
||||
* Some OBBs might have been unmounted when this volume was
|
||||
* unmounted, so send a message to the handler to let it know to
|
||||
* remove those from the list of mounted OBBS.
|
||||
*/
|
||||
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
|
||||
path));
|
||||
} else if (Environment.MEDIA_MOUNTED.equals(state)) {
|
||||
mPms.updateExternalMediaStatus(true, false);
|
||||
if (path.equals(mExternalStoragePath)) {
|
||||
// Update state on PackageManager, but only of real events
|
||||
if (!mEmulateExternalStorage) {
|
||||
if (Environment.MEDIA_UNMOUNTED.equals(state)) {
|
||||
mPms.updateExternalMediaStatus(false, false);
|
||||
|
||||
/*
|
||||
* Some OBBs might have been unmounted when this volume was
|
||||
* unmounted, so send a message to the handler to let it know to
|
||||
* remove those from the list of mounted OBBS.
|
||||
*/
|
||||
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
|
||||
OBB_FLUSH_MOUNT_STATE, path));
|
||||
} else if (Environment.MEDIA_MOUNTED.equals(state)) {
|
||||
mPms.updateExternalMediaStatus(true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String oldState = mLegacyState;
|
||||
mLegacyState = state;
|
||||
|
||||
synchronized (mListeners) {
|
||||
for (int i = mListeners.size() -1; i >= 0; i--) {
|
||||
MountServiceBinderListener bl = mListeners.get(i);
|
||||
@ -575,20 +580,15 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
|
||||
/**
|
||||
* Determine media state and UMS detection status
|
||||
*/
|
||||
String path = Environment.getExternalStorageDirectory().getPath();
|
||||
String state = Environment.MEDIA_REMOVED;
|
||||
|
||||
try {
|
||||
String[] vols = mConnector.doListCommand(
|
||||
"volume list", VoldResponseCode.VolumeListResult);
|
||||
for (String volstr : vols) {
|
||||
String[] tok = volstr.split(" ");
|
||||
// FMT: <label> <mountpoint> <state>
|
||||
if (!tok[1].equals(path)) {
|
||||
Slog.w(TAG, String.format(
|
||||
"Skipping unknown volume '%s'",tok[1]));
|
||||
continue;
|
||||
}
|
||||
String path = tok[1];
|
||||
String state = Environment.MEDIA_REMOVED;
|
||||
|
||||
int st = Integer.parseInt(tok[2]);
|
||||
if (st == VolumeState.NoMedia) {
|
||||
state = Environment.MEDIA_REMOVED;
|
||||
@ -603,14 +603,15 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
|
||||
} else {
|
||||
throw new Exception(String.format("Unexpected state %d", st));
|
||||
}
|
||||
}
|
||||
if (state != null) {
|
||||
if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
|
||||
updatePublicVolumeState(path, state);
|
||||
|
||||
if (state != null) {
|
||||
if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
|
||||
updatePublicVolumeState(path, state);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Error processing initial volume state", e);
|
||||
updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
|
||||
updatePublicVolumeState(mExternalStoragePath, Environment.MEDIA_REMOVED);
|
||||
}
|
||||
|
||||
try {
|
||||
@ -1052,11 +1053,12 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
|
||||
public MountService(Context context) {
|
||||
mContext = context;
|
||||
|
||||
mExternalStoragePath = Environment.getExternalStorageDirectory().getPath();
|
||||
mEmulateExternalStorage = context.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_emulateExternalStorage);
|
||||
if (mEmulateExternalStorage) {
|
||||
Slog.d(TAG, "using emulated external storage");
|
||||
mLegacyState = Environment.MEDIA_MOUNTED;
|
||||
mVolumeStates.put(mExternalStoragePath, Environment.MEDIA_MOUNTED);
|
||||
}
|
||||
|
||||
// XXX: This will go away soon in favor of IMountServiceObserver
|
||||
@ -1125,54 +1127,56 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
|
||||
validatePermission(android.Manifest.permission.SHUTDOWN);
|
||||
|
||||
Slog.i(TAG, "Shutting down");
|
||||
synchronized (mVolumeStates) {
|
||||
for (String path : mVolumeStates.keySet()) {
|
||||
String state = mVolumeStates.get(path);
|
||||
|
||||
String path = Environment.getExternalStorageDirectory().getPath();
|
||||
String state = getVolumeState(path);
|
||||
|
||||
if (state.equals(Environment.MEDIA_SHARED)) {
|
||||
/*
|
||||
* If the media is currently shared, unshare it.
|
||||
* XXX: This is still dangerous!. We should not
|
||||
* be rebooting at *all* if UMS is enabled, since
|
||||
* the UMS host could have dirty FAT cache entries
|
||||
* yet to flush.
|
||||
*/
|
||||
setUsbMassStorageEnabled(false);
|
||||
} else if (state.equals(Environment.MEDIA_CHECKING)) {
|
||||
/*
|
||||
* If the media is being checked, then we need to wait for
|
||||
* it to complete before being able to proceed.
|
||||
*/
|
||||
// XXX: @hackbod - Should we disable the ANR timer here?
|
||||
int retries = 30;
|
||||
while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException iex) {
|
||||
Slog.e(TAG, "Interrupted while waiting for media", iex);
|
||||
break;
|
||||
if (state.equals(Environment.MEDIA_SHARED)) {
|
||||
/*
|
||||
* If the media is currently shared, unshare it.
|
||||
* XXX: This is still dangerous!. We should not
|
||||
* be rebooting at *all* if UMS is enabled, since
|
||||
* the UMS host could have dirty FAT cache entries
|
||||
* yet to flush.
|
||||
*/
|
||||
setUsbMassStorageEnabled(false);
|
||||
} else if (state.equals(Environment.MEDIA_CHECKING)) {
|
||||
/*
|
||||
* If the media is being checked, then we need to wait for
|
||||
* it to complete before being able to proceed.
|
||||
*/
|
||||
// XXX: @hackbod - Should we disable the ANR timer here?
|
||||
int retries = 30;
|
||||
while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException iex) {
|
||||
Slog.e(TAG, "Interrupted while waiting for media", iex);
|
||||
break;
|
||||
}
|
||||
state = Environment.getExternalStorageState();
|
||||
}
|
||||
if (retries == 0) {
|
||||
Slog.e(TAG, "Timed out waiting for media to check");
|
||||
}
|
||||
}
|
||||
state = Environment.getExternalStorageState();
|
||||
}
|
||||
if (retries == 0) {
|
||||
Slog.e(TAG, "Timed out waiting for media to check");
|
||||
}
|
||||
}
|
||||
|
||||
if (state.equals(Environment.MEDIA_MOUNTED)) {
|
||||
// Post a unmount message.
|
||||
ShutdownCallBack ucb = new ShutdownCallBack(path, observer);
|
||||
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
|
||||
} else if (observer != null) {
|
||||
/*
|
||||
* Observer is waiting for onShutDownComplete when we are done.
|
||||
* Since nothing will be done send notification directly so shutdown
|
||||
* sequence can continue.
|
||||
*/
|
||||
try {
|
||||
observer.onShutDownComplete(StorageResultCode.OperationSucceeded);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "RemoteException when shutting down");
|
||||
if (state.equals(Environment.MEDIA_MOUNTED)) {
|
||||
// Post a unmount message.
|
||||
ShutdownCallBack ucb = new ShutdownCallBack(path, observer);
|
||||
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
|
||||
} else if (observer != null) {
|
||||
/*
|
||||
* Observer is waiting for onShutDownComplete when we are done.
|
||||
* Since nothing will be done send notification directly so shutdown
|
||||
* sequence can continue.
|
||||
*/
|
||||
try {
|
||||
observer.onShutDownComplete(StorageResultCode.OperationSucceeded);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "RemoteException when shutting down");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1244,16 +1248,15 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
|
||||
* @return state of the volume at the specified mount point
|
||||
*/
|
||||
public String getVolumeState(String mountPoint) {
|
||||
/*
|
||||
* XXX: Until we have multiple volume discovery, just hardwire
|
||||
* this to /sdcard
|
||||
*/
|
||||
if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
|
||||
Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
synchronized (mVolumeStates) {
|
||||
String state = mVolumeStates.get(mountPoint);
|
||||
if (state == null) {
|
||||
Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
return mLegacyState;
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isExternalStorageEmulated() {
|
||||
|
Reference in New Issue
Block a user