大容量存储的接口在StorageManager中的代码如下:
public void enableUsbMassStorage() {
try {
mMountService.setUsbMassStorageEnabled(true);
} catch (Exception ex) {
Log.e(TAG, "Failed to enable UMS", ex);
}
}
/**
* Disables USB Mass Storage (UMS) on the device.
*
* @hide
*/
public void disableUsbMassStorage() {
try {
mMountService.setUsbMassStorageEnabled(false);
} catch (Exception ex) {
Log.e(TAG, "Failed to disable UMS", ex);
}
}
在MountService中的接口
public void setUsbMassStorageEnabled(boolean enable) {
waitForReady();
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
validateUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
final StorageVolume primary = getPhysicalVolume();
if (primary == null){
return;
}
// TODO: Add support for multiple share methods
/*
* If the volume is mounted and we're enabling then unmount it
*/
String path = primary.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);//关闭大容量,关闭之后还有重新挂载sd卡
if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
Slog.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.
*/
}
}
}
接下来先看doShareUnshareVolume接口
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));
}
try {
mConnector.execute("volume", enable ? "share" : "unshare", path, method);//执行命令
} catch (NativeDaemonConnectorException e) {
Slog.e(TAG, "Failed to share/unshare", e);
}
}
而当执行开启大容量时,先是发送了一个消息,我们来看下,顺便也分析下MountService中的消息机制。
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);
}
我们再来分析下,MountService中的消息,发送H_UNMOUNT_PM_UPDATE消息后,handler处理,然后将其UnmountCallBack对象加入mForceUnmounts列表;
class MountServiceHandler extends Handler {
ArrayList mForceUnmounts = new ArrayList();
boolean mUpdatingStatus = false;
MountServiceHandler(Looper l) {
super(l);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case H_UNMOUNT_PM_UPDATE: {
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
mForceUnmounts.add(ucb);
if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
// Register only if needed.
if (!mUpdatingStatus) {
if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
mUpdatingStatus = true;
mPms.updateExternalMediaStatus(false, true);
}
break;
}
case H_UNMOUNT_PM_DONE: {
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
mUpdatingStatus = false;
int size = mForceUnmounts.size();
int sizeArr[] = new int[size];
int sizeArrN = 0;
// Kill processes holding references first
ActivityManagerService ams = (ActivityManagerService)
ServiceManager.getService("activity");
for (int i = 0; i < size; i++) {
UnmountCallBack ucb = mForceUnmounts.get(i);
String path = ucb.path;
boolean done = false;
if (!ucb.force) {
done = true;
} else {
int pids[] = getStorageUsers(path);
if (pids == null || pids.length == 0) {
done = true;
} else {
// Eliminate system process here?
ams.killPids(pids, "unmount media", true);
// Confirm if file references have been freed.
pids = getStorageUsers(path);
if (pids == null || pids.length == 0) {
done = true;
}
}
}
if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
// Retry again
Slog.i(TAG, "Retrying to kill storage users again");
mHandler.sendMessageDelayed(
mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
ucb.retries++),
RETRY_UNMOUNT_DELAY);
} else {
if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
Slog.i(TAG, "Failed to unmount media inspite of " +
MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
}
sizeArr[sizeArrN++] = i;
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
ucb));
}
}
// Remove already processed elements from list.
for (int i = (sizeArrN-1); i >= 0; i--) {
mForceUnmounts.remove(sizeArr[i]);
}
break;
}
case H_UNMOUNT_MS: {
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
ucb.handleFinished();
break;
}
hander处理H_UNMOUNT_PM_UPDATE消息,最后会调用PMS的updateExternalMediaStatus接口,我们不去分析其作用,看看它发送了UPDATED_MEDIA_STATUS这个消息
@Override
public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
throw new SecurityException("Media status can only be updated by the system");
}
// reader; this apparently protects mMediaMounted, but should probably
// be a different lock in that case.
synchronized (mPackages) {
Log.i(TAG, "Updating external media status from "
+ (mMediaMounted ? "mounted" : "unmounted") + " to "
+ (mediaStatus ? "mounted" : "unmounted"));
if (DEBUG_SD_INSTALL)
Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + mediaStatus
+ ", mMediaMounted=" + mMediaMounted);
if (mediaStatus == mMediaMounted) {
final Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1
: 0, -1);
mHandler.sendMessage(msg);//发送消息
return;
}
mMediaMounted = mediaStatus;
}
// Queue up an async operation since the package installation may take a
// little while.
mHandler.post(new Runnable() {
public void run() {
updateExternalMediaStatusInner(mediaStatus, reportStatus, true);
}
});
}
再看看PMS的handler处理,调用了MountService的finishMediaUpdate方法
case UPDATED_MEDIA_STATUS: {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Got message UPDATED_MEDIA_STATUS");
boolean reportStatus = msg.arg1 == 1;
boolean doGc = msg.arg2 == 1;
if (DEBUG_SD_INSTALL) Log.i(TAG, "reportStatus=" + reportStatus + ", doGc = " + doGc);
if (doGc) {
// Force a gc to clear up stale containers.
Runtime.getRuntime().gc();
}
if (msg.obj != null) {
@SuppressWarnings("unchecked")
Set args = (Set) msg.obj;
if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading all containers");
// Unload containers
unloadAllContainers(args);
}
if (reportStatus) {
try {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Invoking MountService call back");
PackageHelper.getMountService().finishMediaUpdate();
} catch (RemoteException e) {
Log.e(TAG, "MountService not running?");
}
}
} break;
而在MountService中最后又去发送了H_UNMOUNT_PM_DONE消息
public void finishMediaUpdate() {
mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
}
下面是handler对H_UNMOUNT_PM_DONE消息的处理
case H_UNMOUNT_PM_DONE: {
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
mUpdatingStatus = false;
int size = mForceUnmounts.size();
int sizeArr[] = new int[size];
int sizeArrN = 0;
// Kill processes holding references first
ActivityManagerService ams = (ActivityManagerService)
ServiceManager.getService("activity");
for (int i = 0; i < size; i++) {//遍历所有的mForceUnmounts
UnmountCallBack ucb = mForceUnmounts.get(i);
String path = ucb.path;
boolean done = false;
if (!ucb.force) {//ucb中如果设置的force是true代表哪个应用如果使用了该volume的路径,强制kill
done = true;
} else {
int pids[] = getStorageUsers(path);
if (pids == null || pids.length == 0) {//如果没有pid,使用该volume路径
done = true;
} else {
// Eliminate system process here?
ams.killPids(pids, "unmount media", true);
// Confirm if file references have been freed.
pids = getStorageUsers(path);
if (pids == null || pids.length == 0) {
done = true;//没有使用了done就为true
}
}
}
if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
// Retry again
Slog.i(TAG, "Retrying to kill storage users again");
mHandler.sendMessageDelayed(//如果done还为true,延迟一段再发,知道次数到MAX_UNMOUNT_RETRIES
mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
ucb.retries++),
RETRY_UNMOUNT_DELAY);
} else {
if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
Slog.i(TAG, "Failed to unmount media inspite of " +
MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
}
sizeArr[sizeArrN++] = i;
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
ucb));//否则就发送H_UNMOUNT_MS消息
}
}
// Remove already processed elements from list.
for (int i = (sizeArrN-1); i >= 0; i--) {
mForceUnmounts.remove(sizeArr[i]);
}
break;
}
H_UNMOUNT_MS消息就是执行ucb的handleFinished函数。
case H_UNMOUNT_MS: {
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
ucb.handleFinished();
break;
}
我们再来看看前面开启大容量里new的UmsEnableCallBack对象
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);
}
其handleFinished接口就是调用了doShareUnshareVolume,向vold发送share命令。
class UmsEnableCallBack extends UnmountCallBack {
final String method;
UmsEnableCallBack(String path, String method, boolean force) {
super(path, force, false);
this.method = method;
}
@Override
void handleFinished() {
super.handleFinished();
doShareUnshareVolume(path, method, true);
}
}
而MountService中只有Unmount和开启大容量存储的时候,会发这个消息。说明这两个动作不允许其他pid,用该volume下的path。而在vold中,Unmount的时候也会对所有使用该path的pid,进行kill;但是开启大容量存储是不会检查哪个pid正在使用该volume的path的。
接下来再来看看开启大容量存储vold和MountService的交互:
vold收到MountService发来的share命令,从CommandListener中分发命令,最后到VolumeManager::shareVolume
int VolumeManager::shareVolume(const char *label, const char *method) {
Volume *v = lookupVolume(label);//找到volume
if (!v) {
errno = ENOENT;
return -1;
}
/*
* Eventually, we'll want to support additional share back-ends,
* some of which may work while the media is mounted. For now,
* we just support UMS
*/
if (strcmp(method, "ums")) {
errno = ENOSYS;
return -1;
}
if (v->getState() == Volume::State_NoMedia) {
errno = ENODEV;
return -1;
}
if (v->getState() != Volume::State_Idle) {
// You need to unmount manually befoe sharing
errno = EBUSY;
return -1;
}
if (mVolManagerDisabled) {
errno = EBUSY;
return -1;
}
dev_t d = v->getShareDevice();
if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
// This volume does not support raw disk access
errno = EINVAL;
return -1;
}
int fd;
char nodepath[255];
int written = snprintf(nodepath,//nodepath写入节点的内容
sizeof(nodepath), "/dev/block/vold/%d:%d",
major(d), minor(d));//
if ((written < 0) || (size_t(written) >= sizeof(nodepath))) {
SLOGE("shareVolume failed: couldn't construct nodepath");
return -1;
}
if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) {
SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
return -1;
}
if (write(fd, nodepath, strlen(nodepath)) < 0) {//往"/sys/class/android_usb/android0/f_mass_storage/lun/file"这个路径下写
SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
close(fd);
return -1;
}
close(fd);
v->handleVolumeShared();//只是将状态改成State_Shared,且发送到MountService。代码就是setState(Volume::State_Shared);
if (mUmsSharingCount++ == 0) {
FILE* fp;
mSavedDirtyRatio = -1; // in case we fail
if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) {
char line[16];
if (fgets(line, sizeof(line), fp) && sscanf(line, "%d", &mSavedDirtyRatio)) {
fprintf(fp, "%d\n", mUmsDirtyRatio);
} else {
SLOGE("Failed to read dirty_ratio (%s)", strerror(errno));
}
fclose(fp);
} else {
SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno));
}
}
return 0;
}
而MountService在OnEvent收到vold发来的State_Shared状态,会调用notifyVolumeStateChange函数,注意在updatePublicVolumeState这个函数中会通知各个listener
} else if (newState == VolumeState.Shared) {
if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
/* Send the media unmounted event first */
updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);//先将 状态改成MEDIA_UNMOUNTED,
sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
updatePublicVolumeState(volume, Environment.MEDIA_SHARED);//然后再设置MEDIA_SHARED
action = Intent.ACTION_MEDIA_SHARED;
if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
}
再来看看vold对unshare的处理,只是在那个路径下写了一个0,发送上层一个State_Idle状态
int VolumeManager::unshareVolume(const char *label, const char *method) {
Volume *v = lookupVolume(label);
if (!v) {
errno = ENOENT;
return -1;
}
if (strcmp(method, "ums")) {
errno = ENOSYS;
return -1;
}
if (v->getState() != Volume::State_Shared) {
errno = EINVAL;
return -1;
}
int fd;
if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) {//同样打开这个路径
SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
return -1;
}
char ch = 0;
if (write(fd, &ch, 1) < 0) {//只是写一个0
SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
close(fd);
return -1;
}
close(fd);
v->handleVolumeUnshared();//就是setState(Volume::State_Idle);
if (--mUmsSharingCount == 0 && mSavedDirtyRatio != -1) {
FILE* fp;
if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) {
fprintf(fp, "%d\n", mSavedDirtyRatio);
fclose(fp);
} else {
SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno));
}
mSavedDirtyRatio = -1;
}
return 0;
}
MountService先发送一个广播,然后再把Volume的状态设置MEDIA_UNMOUNTED
if (oldState == VolumeState.Shared && newState != oldState) {
if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, volume, UserHandle.ALL);//发送广播
}
if (newState == VolumeState.Init) {
} else if (newState == VolumeState.NoMedia) {
// NoMedia is handled via Disk Remove events
} else if (newState == VolumeState.Idle) {
/*
* Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
* if we're in the process of enabling UMS
*/
if (!state.equals(
Environment.MEDIA_BAD_REMOVAL) && !state.equals(
Environment.MEDIA_NOFS) && !state.equals(
Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
action = Intent.ACTION_MEDIA_UNMOUNTED;
}
}
查看有没有开启大容量
private boolean doGetVolumeShared(String path, String method) {
final NativeDaemonEvent event;
try {
event = mConnector.execute("volume", "shared", path, method);
} catch (NativeDaemonConnectorException ex) {
Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
return false;
}
if (event.getCode() == VoldResponseCode.ShareEnabledResult) {
return event.getMessage().endsWith("enabled");
} else {
return false;
}
}
vold就看Volume的状态是否是State_Shared
int VolumeManager::shareEnabled(const char *label, const char *method, bool *enabled) {
Volume *v = lookupVolume(label);
if (!v) {
errno = ENOENT;
return -1;
}
if (strcmp(method, "ums")) {
errno = ENOSYS;
return -1;
}
if (v->getState() != Volume::State_Shared) {
*enabled = false;
} else {
*enabled = true;
}
return 0;
}
MountService中的消息机制很有借鉴意义,可以利用消息机制,做一个错误延时处理。