大容量存储的接口在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<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>(); 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<AsecInstallArgs> args = (Set<AsecInstallArgs>) 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中的消息机制很有借鉴意义,可以利用消息机制,做一个错误延时处理。