在我的博客里,已经对vold和MountService分析过了。http://blog.csdn.net/kc58236582/article/details/46122979
这里在进一步分析。重复的就不再分析了:
main函数里面主要创建了NetlinkManager、VolumeManager、CommandListener这都在另一个博客中分析过了,这里我们主要分析下process_config函数:
int main() { VolumeManager *vm; CommandListener *cl; NetlinkManager *nm; SLOGI("Vold 2.1 (the revenge) firing up"); mkdir("/dev/block/vold", 0755); /* For when cryptfs checks and mounts an encrypted filesystem */ klog_set_level(6); /* Create our singleton managers */ if (!(vm = VolumeManager::Instance())) { SLOGE("Unable to create VolumeManager"); exit(1); }; if (!(nm = NetlinkManager::Instance())) { SLOGE("Unable to create NetlinkManager"); exit(1); }; cl = new CommandListener(); vm->setBroadcaster((SocketListener *) cl); nm->setBroadcaster((SocketListener *) cl); if (vm->start()) { SLOGE("Unable to start VolumeManager (%s)", strerror(errno)); exit(1); } if (process_config(vm)) { SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno)); } if (nm->start()) { SLOGE("Unable to start NetlinkManager (%s)", strerror(errno)); exit(1); } coldboot("/sys/block"); // coldboot("/sys/class/switch"); /* * Now that we're up, we can respond to commands */ if (cl->startListener()) { SLOGE("Unable to start CommandListener (%s)", strerror(errno)); exit(1); } // Eventually we'll become the monitoring thread while(1) { sleep(1000); } SLOGI("Vold exiting"); exit(0); }
process_config函数主要对fstab文件进行解析,然后创建DirectVolume对象,加入VolumManager
static int process_config(VolumeManager *vm) { char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)]; char propbuf[PROPERTY_VALUE_MAX]; int i; int ret = -1; int flags; property_get("ro.hardware", propbuf, ""); snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf); fstab = fs_mgr_read_fstab(fstab_filename); if (!fstab) { SLOGE("failed to open %s\n", fstab_filename); return -1; } /* Loop through entries looking for ones that vold manages */ for (i = 0; i < fstab->num_entries; i++) { if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {//看下面的文件主要对<span style="background-color: rgb(255, 255, 51);">voldmanaged进行解析</span> DirectVolume *dv = NULL; flags = 0; /* Set any flags that might be set for this volume */ if (fs_mgr_is_nonremovable(&fstab->recs[i])) { flags |= VOL_NONREMOVABLE; } if (fs_mgr_is_encryptable(&fstab->recs[i])) { flags |= VOL_ENCRYPTABLE; } /* Only set this flag if there is not an emulated sd card */ if (fs_mgr_is_noemulatedsd(&fstab->recs[i]) && !strcmp(fstab->recs[i].fs_type, "vfat")) { flags |= VOL_PROVIDES_ASEC; } dv = new DirectVolume(vm, &(fstab->recs[i]), flags); if (dv->addPath(fstab->recs[i].blk_device)) {//将fstab文件中设备的地址add到DirectVolume中 SLOGE("Failed to add devpath %s to volume %s", fstab->recs[i].blk_device, fstab->recs[i].label); goto out_fail; } vm->addVolume(dv);//加入到VolumManager中 } } ret = 0; out_fail: return ret; }
fstab文件如下,fstab文件的介绍在我的另一篇博客中http://blog.csdn.net/kc58236582/article/details/47053049
/dev/block/platform/comip-mmc.1/by-name/system /system ext4 ro,barrier=1 wait
/dev/block/platform/comip-mmc.1/by-name/cache /cache ext4 noatime,nosuid,nodev,barrier=1,data=ordered wait,check
/dev/block/platform/comip-mmc.1/by-name/userdata /data ext4 noatime,nosuid,nodev,barrier=1,data=ordered,noauto_da_alloc wait,check,encryptable=footer
#/dev/block/platform/comip-mmc.1/by-name/amt /amt ext4 rw wait
/devices/platform/comip-mmc.0/mmc_host/mmc1 auto vfat defaults voldmanaged=sdcard1:auto,noemulatedsd
/devices/platform/comip-hcd/usb1 /mnt/media_rw/usbotg vfat defaults voldmanaged=usbotg:auto,noemulatedsd
/dev/block/mmcblk1p1 /sdcard vfat defaults recoveryonly
/dev/block/platform/comip-mmc.1/by-name/kernel /kernel emmc defaults defaults
/dev/block/platform/comip-mmc.1/by-name/ramdisk /boot emmc defaults defaults
/dev/block/platform/comip-mmc.1/by-name/ramdisk_recovery /recovery emmc defaults defaults
/dev/block/platform/comip-mmc.1/by-name/ramdisk_amt1 /ramdisk_amt1 emmc defaults defaults
/dev/block/platform/comip-mmc.1/by-name/ramdisk_amt3 /ramdisk_amt3 emmc defaults defaults
/dev/block/platform/comip-mmc.1/by-name/kernel_recovery /kernel_recovery emmc defaults defaults
/dev/block/platform/comip-mmc.1/by-name/logo /logo emmc defaults defaults
/dev/block/platform/comip-mmc.1/by-name/misc /misc emmc defaults defaults
/dev/block/platform/comip-mmc.1/by-name/fota /fota emmc defaults defaults
/dev/block/platform/comip-mmc.1/by-name/modemarm /modemarm emmc defaults defaults
/dev/block/platform/comip-mmc.1/by-name/modemdsp /modemdsp emmc defaults defaults
/dev/block/mmcblk0boot0 /uboot emmc defaults defaults
/dev/block/platform/comip-mmc.1/by-name/lcboot /lcboot emmc defaults defaults
/dev/block/zram0 none swap defaults zramsize=268435456
我们再来看看DirectVolume的构造函数,会将fstab文件的label初始化mMountpoint 和mFuseMountpoint ,上面文件的两个label分别为sdcard1、usbotg
DirectVolume::DirectVolume(VolumeManager *vm, const fstab_rec* rec, int flags) : Volume(vm, rec, flags) { mPaths = new PathCollection(); for (int i = 0; i < MAX_PARTITIONS; i++) mPartMinors[i] = -1; mPendingPartCount = 0; mDiskMajor = -1; mDiskMinor = -1; mDiskNumParts = 0; mIsDecrypted = 0; if (strcmp(rec->mount_point, "auto") != 0) { ALOGE("Vold managed volumes must have auto mount point; ignoring %s", rec->mount_point); } char mount[PATH_MAX]; snprintf(mount, PATH_MAX, "%s/%s", Volume::MEDIA_DIR, rec->label);// mMountpoint = strdup(mount); snprintf(mount, PATH_MAX, "%s/%s", Volume::FUSE_DIR, rec->label); mFuseMountpoint = strdup(mount); setState(Volume::State_NoMedia); }
Volume::MEDIA_DIR Volume::FUSE_DIR的定义如下:
/* * Media directory - stuff that only media_rw user can see */ const char *Volume::MEDIA_DIR = "/mnt/media_rw"; /* * Fuse directory - location where fuse wrapped filesystems go */ const char *Volume::FUSE_DIR = "/storage";
这样fstab文件有两项,所以我们有两个DirectVolume对象:
当有设备插入的时候,内核会通知我们,调用onEvent函数:
void NetlinkHandler::onEvent(NetlinkEvent *evt) { VolumeManager *vm = VolumeManager::Instance(); const char *subsys = evt->getSubsystem(); if (!subsys) { SLOGW("No subsystem found in netlink event"); return; } if (!strcmp(subsys, "block")) { vm->handleBlockEvent(evt); } }
调用VolumeManager::handleBlockEvent函数
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) { const char *devpath = evt->findParam("DEVPATH");//获取设备的地址 /* Lookup a volume to handle this device */ VolumeCollection::iterator it; bool hit = false; for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {//遍历所有的volume if (!(*it)->handleBlockEvent(evt)) {//调用每个volume的handleBlockEvent #ifdef NETLINK_DEBUG SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel()); #endif hit = true; break; } } if (!hit) { #ifdef NETLINK_DEBUG SLOGW("No volumes handled block event for '%s'", devpath); #endif } }
DirectVolume::handleBlockEvent函数分析:
int DirectVolume::handleBlockEvent(NetlinkEvent *evt) { const char *dp = evt->findParam("DEVPATH");//获取设备地址 PathCollection::iterator it; for (it = mPaths->begin(); it != mPaths->end(); ++it) { if ((*it)->match(dp)) {//如果和mpath中匹配 /* We can handle this disk */ int action = evt->getAction(); const char *devtype = evt->findParam("DEVTYPE"); if (action == NetlinkEvent::NlActionAdd) { int major = atoi(evt->findParam("MAJOR")); int minor = atoi(evt->findParam("MINOR")); char nodepath[255]; snprintf(nodepath, sizeof(nodepath), "/dev/block/vold/%d:%d", major, minor); if (createDeviceNode(nodepath, major, minor)) { SLOGE("Error making device node '%s' (%s)", nodepath, strerror(errno)); } if (!strcmp(devtype, "disk")) { handleDiskAdded(dp, evt); } else { handlePartitionAdded(dp, evt); } /* Send notification iff disk is ready (ie all partitions found) */ if (getState() == Volume::State_Idle) { char msg[255]; snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)", getLabel(), getFuseMountpoint(), mDiskMajor, mDiskMinor); mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,//发到MountService,VolumeDiskInserted msg, false); } } else if (action == NetlinkEvent::NlActionRemove) { if (!strcmp(devtype, "disk")) { handleDiskRemoved(dp, evt); } else { handlePartitionRemoved(dp, evt); } } else if (action == NetlinkEvent::NlActionChange) { if (!strcmp(devtype, "disk")) { handleDiskChanged(dp, evt); } else { handlePartitionChanged(dp, evt); } } else { SLOGW("Ignoring non add/remove/change event"); } return 0; } } errno = ENODEV; return -1; }
因此我们下面首先来介绍下MountService,然后再来分析vold发来的VolumeDiskInserted状态是怎么处理的.
MountService在构造函数里面会去调用readStorageListLocked函数:
public MountService(Context context) { sSelf = this; mContext = context; synchronized (mVolumesLock) { readStorageListLocked(); }
下面我们详细分析下readStorageListLocked函数:
private void readStorageListLocked() { mVolumes.clear(); mVolumeStates.clear(); Resources resources = mContext.getResources(); int id = com.android.internal.R.xml.storage_list; XmlResourceParser parser = resources.getXml(id); AttributeSet attrs = Xml.asAttributeSet(parser); try { XmlUtils.beginDocument(parser, TAG_STORAGE_LIST); while (true) { XmlUtils.nextElement(parser); String element = parser.getName(); if (element == null) break; if (TAG_STORAGE.equals(element)) { TypedArray a = resources.obtainAttributes(attrs, com.android.internal.R.styleable.Storage); String path = a.getString( com.android.internal.R.styleable.Storage_mountPoint); int descriptionId = a.getResourceId( com.android.internal.R.styleable.Storage_storageDescription, -1); CharSequence description = a.getText( com.android.internal.R.styleable.Storage_storageDescription); boolean primary = a.getBoolean( com.android.internal.R.styleable.Storage_primary, false); boolean removable = a.getBoolean( com.android.internal.R.styleable.Storage_removable, false); boolean emulated = a.getBoolean( com.android.internal.R.styleable.Storage_emulated, false); int mtpReserve = a.getInt( com.android.internal.R.styleable.Storage_mtpReserve, 0); boolean allowMassStorage = a.getBoolean( com.android.internal.R.styleable.Storage_allowMassStorage, false); // resource parser does not support longs, so XML value is in megabytes long maxFileSize = a.getInt( com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L; Slog.d(TAG, "got storage path: " + path + " description: " + description + " primary: " + primary + " removable: " + removable + " emulated: " + emulated + " mtpReserve: " + mtpReserve + " allowMassStorage: " + allowMassStorage + " maxFileSize: " + maxFileSize); //上面都是对xml文件的解析过程,我们看看下面的xml文件 if (emulated) {//如果emulated为true // For devices with emulated storage, we create separate // volumes for each known user. mEmulatedTemplate = new StorageVolume(null, descriptionId, true, false, true, mtpReserve, false, maxFileSize, null); final UserManagerService userManager = UserManagerService.getInstance(); for (UserInfo user : userManager.getUsers(false)) { createEmulatedVolumeForUserLocked(user.getUserHandle()); } } else { if (path == null || description == null) { Slog.e(TAG, "Missing storage path or description in readStorageList"); } else { final StorageVolume volume = new StorageVolume(new File(path), descriptionId, primary, removable, emulated, mtpReserve, allowMassStorage, maxFileSize, null); addVolumeLocked(volume);//调用addVolumeLocked函数 // Until we hear otherwise, treat as unmounted mVolumeStates.put(volume.getPath(), Environment.MEDIA_UNMOUNTED);//mVolumeStates会把所有的path的状态保存下来,比较重要。getVolumState都是从这个成员变量中取 volume.setState(Environment.MEDIA_UNMOUNTED);//状态置为Environment.MEDIA_UNMOUNTED } } a.recycle(); } } } catch (XmlPullParserException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } finally { // Compute storage ID for each physical volume; emulated storage is // always 0 when defined. int index = isExternalStorageEmulated() ? 1 : 0; for (StorageVolume volume : mVolumes) { if (!volume.isEmulated()) { volume.setStorageId(index++); } } parser.close(); } }
storage_list.xml文件如下:
<StorageList xmlns:android="http://schemas.android.com/apk/res/android"> <!-- removable is not set in nosdcard product --> <!--use for default --> <storage android:mountPoint="/storage/sdcard0" android:storageDescription="@string/storage_internal" android:primary="true" android:emulated="true" android:allowMassStorage="false" android:removable="false" /> <storage android:mountPoint="/storage/sdcard1" android:storageDescription="@string/storage_sd_card" android:primary="false" android:removable="true" android:emulated="false" android:allowMassStorage="true" android:mtpReserve="0" /> <storage android:mountPoint="/storage/usbotg" android:storageDescription="@string/storage_usb_otg" android:primary="false" android:removable="true" android:emulated="false" android:allowMassStorage="false" />createEmulatedVolumeForUserLocked函数会针对多用户,详细不分析,最后的地址/storage/emulated/0,其中0代表userId
private void createEmulatedVolumeForUserLocked(UserHandle user) { if (mEmulatedTemplate == null) { throw new IllegalStateException("Missing emulated volume multi-user template"); } final UserEnvironment userEnv = new UserEnvironment(user.getIdentifier()); final File path = userEnv.getExternalStorageDirectory(); final StorageVolume volume = StorageVolume.fromTemplate(mEmulatedTemplate, path, user); volume.setStorageId(0); addVolumeLocked(volume);//调用addVolumeLocked if (mSystemReady) { updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);//状态置为Environment.MEDIA_MOUNTED,因为这是内置sd卡 } else { // Place stub status for early callers to find mVolumeStates.put(volume.getPath(), Environment.MEDIA_MOUNTED); volume.setState(Environment.MEDIA_MOUNTED); } }
addVolumeLocked函数如下:将volume加到mVolumes,并且将volume的path加到mVolumesByPath
private void addVolumeLocked(StorageVolume volume) { Slog.d(TAG, "addVolumeLocked() " + volume); mVolumes.add(volume); final StorageVolume existing = mVolumesByPath.put(volume.getPath(), volume); if (existing != null) { throw new IllegalStateException( "Volume at " + volume.getPath() + " already exists: " + existing); } }
回到前面vold收到内核有检测到外部存储信息,vold往MountService发送VolumeDiskInserted状态,MountService会在onEvent中收到消息:
public boolean onEvent(int code, String raw, String[] cooked) { ..... else if ((code == VoldResponseCode.VolumeDiskInserted) || (code == VoldResponseCode.VolumeDiskRemoved) || (code == VoldResponseCode.VolumeBadRemoval)) { // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>) // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>) // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>) String action = null; final String label = cooked[2]; final String path = cooked[3]; int major = -1; int minor = -1; try { String devComp = cooked[6].substring(1, cooked[6].length() -1); String[] devTok = devComp.split(":"); major = Integer.parseInt(devTok[0]); minor = Integer.parseInt(devTok[1]); } catch (Exception ex) { Slog.e(TAG, "Failed to parse major/minor", ex); } final StorageVolume volume; final String state; synchronized (mVolumesLock) { volume = mVolumesByPath.get(path); state = mVolumeStates.get(path); } if (code == VoldResponseCode.VolumeDiskInserted) {//如果是VolumeDiskInserted,直接开个线程调用doMountVolume函数 new Thread("MountService#VolumeDiskInserted") { @Override public void run() { try { int rc; if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) { Slog.w(TAG, String.format("Insertion mount failed (%d)", rc)); } } catch (Exception ex) { Slog.w(TAG, "Failed to mount media on insertion", ex); } } }.start(); } ........
doMountVolume函数如下:
private int doMountVolume(String path) { int rc = StorageResultCode.OperationSucceeded; final StorageVolume volume; synchronized (mVolumesLock) { volume = mVolumesByPath.get(path); } if (!volume.isEmulated() && hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA)) { Slog.w(TAG, "User has restriction DISALLOW_MOUNT_PHYSICAL_MEDIA; cannot mount volume."); return StorageResultCode.OperationFailedInternalError; } if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path); try { mConnector.execute("volume", "mount", path);//mConnector是和vold通信的就不分析了 } catch (NativeDaemonConnectorException e) {//下面都是一些异常 /* * Mount failed for some reason */ String action = null; int code = e.getCode(); if (code == VoldResponseCode.OpFailedNoMedia) { /* * Attempt to mount but no media inserted */ rc = StorageResultCode.OperationFailedNoMedia; } else if (code == VoldResponseCode.OpFailedMediaBlank) { if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs"); /* * Media is blank or does not contain a supported filesystem */ updatePublicVolumeState(volume, Environment.MEDIA_NOFS); action = Intent.ACTION_MEDIA_NOFS; rc = StorageResultCode.OperationFailedMediaBlank; } else if (code == VoldResponseCode.OpFailedMediaCorrupt) { if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt"); /* * Volume consistency check failed */ updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTABLE); action = Intent.ACTION_MEDIA_UNMOUNTABLE; rc = StorageResultCode.OperationFailedMediaCorrupt; } else { rc = StorageResultCode.OperationFailedInternalError; } /* * Send broadcast intent (if required for the failure) */ if (action != null) { sendStorageIntent(action, volume, UserHandle.ALL); } } return rc; }
这样又到vold了,首先接受MountService发来的mount信息:
vold收MountService消息,是在CommandListener::VolumeCmd::runCommand函数中
int CommandListener::VolumeCmd::runCommand(SocketClient *cli, int argc, char **argv) { dumpArgs(argc, argv, -1); if (argc < 2) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false); return 0; } VolumeManager *vm = VolumeManager::Instance(); int rc = 0; ....... else if (!strcmp(argv[1], "mount")) { if (argc != 3) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>", false); return 0; } rc = vm->mountVolume(argv[2]);//直接调用volumeManager的mountVolume函数 } ...........
VolumeManager::mountVolume函数:
int VolumeManager::mountVolume(const char *label) { Volume *v = lookupVolume(label);//现在VolumManager中查找volume if (!v) { errno = ENOENT; return -1; } return v->mountVol();//调用DirectVolume的mountVol函数 }
根据上面发下来的地址和getFuseMountpoint来比较,就是以 "/storage"开始的地址
Volume *VolumeManager::lookupVolume(const char *label) { VolumeCollection::iterator i; for (i = mVolumes->begin(); i != mVolumes->end(); ++i) { if (label[0] == '/') { if (!strcmp(label, (*i)->getFuseMountpoint())) return (*i); } else { if (!strcmp(label, (*i)->getLabel())) return (*i); } } return NULL; }
具体的挂载函数不分析了,注意每次状态改变setState的时候都会给MountService发送消息。
如果vold收到内核的VolumeDiskRemoved消息:
void DirectVolume::handleDiskRemoved(const char * /*devpath*/, NetlinkEvent *evt) { int major = atoi(evt->findParam("MAJOR")); int minor = atoi(evt->findParam("MINOR")); char msg[255]; bool enabled; if (mVm->shareEnabled(getLabel(), "ums", &enabled) == 0 && enabled) { mVm->unshareVolume(getLabel(), "ums"); } SLOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor); snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)", getLabel(), getFuseMountpoint(), major, minor); mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved,//发送到MountService msg, false); setState(Volume::State_NoMedia); }
看看MountService的onEvent函数,更新下状态
} else if (code == VoldResponseCode.VolumeDiskRemoved) { /* * This event gets trumped if we're already in BAD_REMOVAL state */ if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) { return true; } /* Send the media unmounted event first */ if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first"); updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED); sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL); if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed"); updatePublicVolumeState(volume, Environment.MEDIA_REMOVED); action = Intent.ACTION_MEDIA_REMOVED; }
Mountservice卸载通过接口unmountVolume
public void unmountVolume(String path, boolean force, boolean removeEncryption) { validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); waitForReady(); String volState = getVolumeState(path); if (DEBUG_UNMOUNT) { Slog.i(TAG, "Unmounting " + path + " force = " + force + " removeEncryption = " + removeEncryption); } if (Environment.MEDIA_UNMOUNTED.equals(volState) || Environment.MEDIA_REMOVED.equals(volState) || Environment.MEDIA_SHARED.equals(volState) || Environment.MEDIA_UNMOUNTABLE.equals(volState)) { // Media already unmounted or cannot be unmounted. // TODO return valid return code when adding observer call back. return; } UnmountCallBack ucb = new UnmountCallBack(path, force, removeEncryption); mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));//是通过发送消息 }
最后再去调用UnmountCallBack 的handleFinished去下发unmount命令
class UnmountCallBack { final String path; final boolean force; final boolean removeEncryption; int retries; UnmountCallBack(String path, boolean force, boolean removeEncryption) { retries = 0; this.path = path; this.force = force; this.removeEncryption = removeEncryption; } void handleFinished() { if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path); doUnmountVolume(path, true, removeEncryption); } }
下面看看vold对unmount的处理:
int VolumeManager::unmountVolume(const char *label, bool force, bool revert) { Volume *v = lookupVolume(label); if (!v) { errno = ENOENT; return -1; } if (v->getState() == Volume::State_NoMedia) { errno = ENODEV; return -1; } if (v->getState() != Volume::State_Mounted) { SLOGW("Attempt to unmount volume which isn't mounted (%d)\n", v->getState()); errno = EBUSY; return UNMOUNT_NOT_MOUNTED_ERR; } cleanupAsec(v, force); return v->unmountVol(force, revert);//MountService发送的参数force都是true,就是强制卸载的 }
Volume::unmountVol函数
int Volume::unmountVol(bool force, bool revert) { int i, rc; int flags = getFlags(); bool providesAsec = (flags & VOL_PROVIDES_ASEC) != 0; if (getState() != Volume::State_Mounted) { SLOGE("Volume %s unmount request when not mounted", getLabel()); errno = EINVAL; return UNMOUNT_NOT_MOUNTED_ERR; } setState(Volume::State_Unmounting);//先将状态置为Volume::State_Unmounting usleep(1000 * 1000); // Give the framework some time to react char service[64]; snprintf(service, 64, "fuse_%s", getLabel()); property_set("ctl.stop", service); /* Give it a chance to stop. I wish we had a synchronous way to determine this... */ sleep(1); // TODO: determine failure mode if FUSE times out if (providesAsec && doUnmount(Volume::SEC_ASECDIR_EXT, force) != 0) { SLOGE("Failed to unmount secure area on %s (%s)", getMountpoint(), strerror(errno)); goto out_mounted; } /* Now that the fuse daemon is dead, unmount it */ if (doUnmount(getFuseMountpoint(), force) != 0) { SLOGE("Failed to unmount %s (%s)", getFuseMountpoint(), strerror(errno)); goto fail_remount_secure; } /* Unmount the real sd card */ if (doUnmount(getMountpoint(), force) != 0) { SLOGE("Failed to unmount %s (%s)", getMountpoint(), strerror(errno)); goto fail_remount_secure; } SLOGI("%s unmounted successfully", getMountpoint()); /* If this is an encrypted volume, and we've been asked to undo * the crypto mapping, then revert the dm-crypt mapping, and revert * the device info to the original values. */ if (revert && isDecrypted()) { cryptfs_revert_volume(getLabel()); revertDeviceInfo(); SLOGI("Encrypted volume %s reverted successfully", getMountpoint()); } setUuid(NULL); setUserLabel(NULL); setState(Volume::State_Idle);//最后成功再讲状态置为Volume::State_Idle mCurrentlyMountedKdev = -1; return 0; fail_remount_secure: if (providesAsec && mountAsecExternal() != 0) { SLOGE("Failed to remount secure area (%s)", strerror(errno)); goto out_nomedia; } out_mounted: setState(Volume::State_Mounted); return -1; out_nomedia: setState(Volume::State_NoMedia); return -1; }
doUnmount函数:
int Volume::doUnmount(const char *path, bool force) { int retries = 10; if (mDebug) { SLOGD("Unmounting {%s}, force = %d", path, force); } while (retries--) {//执行10次 if (!umount(path) || errno == EINVAL || errno == ENOENT) { SLOGI("%s sucessfully unmounted", path);//如果直接成功退出了 return 0; } int action = 0;//如果有进程正在使用该设备就不能卸载。 if (force) {//等待到retries == 1或者2的时候 if (retries == 1) { action = 2; // SIGKILL } else if (retries == 2) { action = 1; // SIGHUP } } SLOGW("Failed to unmount %s (%s, retries %d, action %d)", path, strerror(errno), retries, action); Process::killProcessesWithOpenFiles(path, action);//杀了使用这个路径的进程,杀了之后就可以卸载了 usleep(1000*1000); } errno = EBUSY; SLOGE("Giving up on unmount %s (%s)", path, strerror(errno)); return -1; }
killProcessesWithOpenFiles函数,先使用SIGTERM杀,如果还杀不掉使用SIGKILL杀,SIGKILL可以强制杀
void Process::killProcessesWithOpenFiles(const char *path, int action) { DIR* dir; struct dirent* de; if (!(dir = opendir("/proc"))) { SLOGE("opendir failed (%s)", strerror(errno)); return; } while ((de = readdir(dir))) { int killed = 0; int pid = getPid(de->d_name); char name[PATH_MAX]; if (pid == -1) continue; getProcessName(pid, name, sizeof(name)); char openfile[PATH_MAX]; if (checkFileDescriptorSymLinks(pid, path, openfile, sizeof(openfile))) { SLOGE("Process %s (%d) has open file %s", name, pid, openfile); } else if (checkFileMaps(pid, path, openfile, sizeof(openfile))) { SLOGE("Process %s (%d) has open filemap for %s", name, pid, openfile); } else if (checkSymLink(pid, path, "cwd")) { SLOGE("Process %s (%d) has cwd within %s", name, pid, path); } else if (checkSymLink(pid, path, "root")) { SLOGE("Process %s (%d) has chroot within %s", name, pid, path); } else if (checkSymLink(pid, path, "exe")) { SLOGE("Process %s (%d) has executable path within %s", name, pid, path); } else { continue; } if (action == 1) { SLOGW("Sending SIGHUP to process %d", pid); kill(pid, SIGTERM); } else if (action == 2) { SLOGE("Sending SIGKILL to process %d", pid); kill(pid, SIGKILL); } } closedir(dir); }
MountService收到后调用onEvent函数:
if (code == VoldResponseCode.VolumeStateChange) { /* * One of the volumes we're managing has changed state. * Format: "NNN Volume <label> <path> state changed * from <old_#> (<old_str>) to <new_#> (<new_str>)" */ notifyVolumeStateChange( cooked[2], cooked[3], Integer.parseInt(cooked[7]), Integer.parseInt(cooked[10])); }
notifyVolumeStateChange函数中,收到VolumeState.Idle后对它进行判断后,将状态置成Environment.MEDIA_UNMOUNTED,并且最后会发送广播给应用。
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; } }
还有说下updatePublicVolumeState这个函数,想setting对volume的检测,就不是通过广播的,而是通过在MountService中注册回调。等状态改变后会调用updatePublicVolumeState来改变volume的状态,最后再去遍历通知回调。比如在Setting收到后,可以调用MountService的getVolumeState查询最新的状态。
private void updatePublicVolumeState(StorageVolume volume, String state) { final String path = volume.getPath(); final String oldState; synchronized (mVolumesLock) { oldState = mVolumeStates.put(path, state); volume.setState(state);//设置下volume的状态 } if (state.equals(oldState)) { Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s", state, state, path)); return; } Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")"); // Tell PackageManager about changes to primary volume state, but only // when not emulated. if (volume.isPrimary() && !volume.isEmulated()) { 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); } } synchronized (mListeners) { for (int i = mListeners.size() -1; i >= 0; i--) {//通知注册到MountService的回调。通知哪个path的volume状态改变了 MountServiceBinderListener bl = mListeners.get(i); try { bl.mListener.onStorageStateChanged(path, oldState, state); } catch (RemoteException rex) { Slog.e(TAG, "Listener dead"); mListeners.remove(i); } catch (Exception ex) { Slog.e(TAG, "Listener failed", ex); } } } }