在我的博客里,已经对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);
}
}
}
}