在我的前一遍博文:Androidvold启动篇中解析了vold如何获取内核发送的热插拔消息,并传递到了VolumeManager的handleBlockEvent函数中。
一、VolumeManager对上层传来的消息处理
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) {
if (!(*it)->handleBlockEvent(evt)) {
#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
}
}
代码中VolumeCollection为Volume的集合,在循环体中(*it)->handleBlockEvent(evt)调用的是Volume的handleBlockEvent函数,但是Volume对该函数的实现如下:
int Volume::handleBlockEvent(NetlinkEvent *evt) {
errno = ENOSYS;
return -1;
}
这下我们得回到第一篇的Main.cpp的main函数的process_config中,该函数有这么一段:
dv = new DirectVolume(vm, &(fstab->recs[i]), flags);
if (dv->addPath(fstab->recs[i].blk_device)) {
SLOGE("Failed to add devpath %s to volume %s",
fstab->recs[i].blk_device, fstab->recs[i].label);
goto out_fail;
}
vm->addVolume(dv);
通过此代码可知,从vold.fstab文件中读取到的volume设备都以DirectVolume对象的方式插入到了VolumeManager中进行管理。
而DirectVolume从Volume类派生而来,所以我们就应该去看DirectVolume的handleBlockEvent函数。
二、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 (!strncmp(dp, *it, strlen(*it))) {//根据消息的设备路径过滤volume设备
/* 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,
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;
}
通过以上代码可知,handleBlockEvent函数对消息进行如下处理:
1、根据设备路径过滤了跟消息无关的设备;
2、根据action来判断事件类型,处理了一下三类事件:
NlActionAdd;
NlActionRemove;
NlActionChange;
3、事件类型确定后,调用相关函数做处理,此处只分析handlePartitionAdded函数的主要代码:
if (getState() != Volume::State_Formatting) {
setState(Volume::State_Idle);
if (mRetryMount == true) {
mRetryMount = false;
mountVol();
}
}
此处,实际调用的是mountVol函数,源代码太多,截取关键代码:
挂载代码:
if (Fat::doMount(devicePath, getMountpoint(), false, false, false,
AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
continue;
}
setState(Volume::State_Mounted);
setState的关键代码如下:
mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
msg, false);
此处为通过套接字向Framework层发送volume状态改变的消息,具体是怎么实现的呢?
这里调用了VolumeManager的mBroadcaster变量的sendBroadcast函数,通过该函数调用mClients成员变量的套接字来发送,
而这个mClients以及跟它相关的套接字是如何来的呢?
请看下文分解:vold与Framework的通讯实现原理