在上一篇Android4.4----Vold挂载管理分析USB挂载(二)中分析了Vold如何接收kernel的uevent并传递到VolumeManager的过程,这里继上篇文章继续分析VolumeManager接收到uevent事件后的处理过程!
那我们先分析一下VolumeManager!
一:VolumeManager
VolumeManager就是整个Android 磁盘挂载Vold机制的核心调度,上下连接的中转站!
在system/vold/main.cpp的主函数中
/* Create our singleton managers */
if (!(vm = VolumeManager::Instance())) {//构造VolumeManager
SLOGE("Unable to create VolumeManager");
exit(1);
};
........................
vm->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));
}
VolumeManager::VolumeManager() {
mDebug = false;
mVolumes = new VolumeCollection();//在构造函数中new一个VolumeCollection用来保存Volume的容器
mActiveContainers = new AsecIdCollection();//活动容器,mount/unmount asec obb时的记录
mBroadcaster = NULL;//指向SocketListener,用于发送挂载事件
mUmsSharingCount = 0;
mSavedDirtyRatio = -1;
// set dirty ratio to 0 when UMS is active
mUmsDirtyRatio = 0;
mVolManagerDisabled = 0;
}
加打印看了一下process_config(vm),发现是去解析fstab.madison文件,但是在真正做动作的地方没有跑进去,也就是说main函数里面最主要的就是new了一个VolumeCollection容器
然后接着看上篇接收到kernel的uevent后的动作!
vm->handleBlockEvent(evt);
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
const char *devpath = evt->findParam("DEVPATH");
const char *devtype = evt->findParam("DEVTYPE");
const char *dn = evt->findParam("DEVNAME");
int major = -1;
int minor = -1;
bool isRawDisk = false;
bool isPartition = false;
int partIdx = -1;
bool isSDCard = false;
char device[255];
char uuid[255];
VolumeCollection::iterator it;
bool hit = false;
static bool preDiskChangeEvent = false;
.............
major = atoi(evt->findParam("MAJOR"));
minor = atoi(evt->findParam("MINOR"));
...........
snprintf(device,255,"/dev/block/vold/%d:%d",major,minor);
SLOGD("Javen.tang handlerBlockEvent major = %d, minor = %d, device = %s, devtype = %s\n", major, minor, device ,devtype);
...........
if (isRawDisk || isPartition) {
//first find uuid from cache
UUIDCache::Entry *entry = uuidCache.searchEntry(device);
if (evt->getAction() == NetlinkEvent::NlActionAdd) {
mode_t mode = 0660 | S_IFBLK;
dev_t dev = (major << 8) | minor;
//if device has been now added, not add again
if (entry != NULL && entry->uuid != NULL) {
return;
}
if (mknod(device, mode, dev) < 0) {//创建设备索引点
if (errno != EEXIST) {
return ;
}
}
if (!getVolumeUUID(device, uuid, 255)) {//根据device获取一个uuid
#ifdef NETLINK_DEBUG
SLOGD("can not get the uuid of %s when device add",device);
#endif
return ;
}
........
if (!hit) {
static char index = 'a'-1;
char * mountPoint = NULL;
const char *dp = NULL;
Volume *volume = NULL;
#ifdef NETLINK_DEBUG
SLOGW("No volumes handled block event for '%s'", devpath);
#endif
if (evt->getAction() != NetlinkEvent::NlActionAdd) {
return;
}
..........
} else {
if (preDiskChangeEvent) {
++index;
preDiskChangeEvent = false;
}
}
...........
else if (isPartition) {
asprintf(&mountPoint,"/mnt/usb/sd%c%i",index,partIdx);//U盘的挂载点
}
............
volume = new DirectVolume(this, &rec, flags);
addVolume(volume);//根据挂载点new一个volume,然后add到mVolumes容器中去
...........
if ( volume->handleBlockEvent(evt) !=0 ) {//然后执行volume->handleBlockEvent(evt)
SLOGD("New add volume fail to handle the event of %s",devpath);
}
注意当我们插上U盘时,会创建两个索引点,一个disk,一个partition,我们真正挂载的是partition
D/Vold ( 976): Javen.tang handlerBlockEvent major = 8, minor = 0, device = /dev/block/vold/8:0, devtype = disk
D/Vold ( 976): Javen.tang handlerBlockEvent major = 8, minor = 1, device = /dev/block/vold/8:1, devtype = partition
也就是说在VolumeManager中的handleBlockEvent主要做了这几件事
1、根据主次设备号创建索引点;
2、寻找挂载点,这里U盘的挂载点为/mnt/usb/sd%c%i;
3、根据挂载点创建一个DirectVolume,DirectVolume是继承于Volume,Volume就想当于一个存储设备,new了一个Volume后add到mVolumes这个容器中去;
4、调用volume的handleBlockEvent(evt)函数;
二:DirectVolume
接着看看DirectVolume的handleBlockEvent(evt)函数!
if (!strcmp(devtype, "disk")) {
handleDiskAdded(dp, evt);
} else {
handlePartitionAdded(dp, evt);
}
void DirectVolume::handlePartitionAdded(const char *devpath, NetlinkEvent *evt) {
int major = atoi(evt->findParam("MAJOR"));
int minor = atoi(evt->findParam("MINOR"));
// MStar Android Patch Begin
char msg[255];
// MStar Android Patch End
int part_num;
//devpath = /devices/platform/Mstar-ehci-1.0/usb4/4-1/4-1:1.0/host1/target1:0:0/1:0:0:0/block/sda/sda1
const char *tmp = evt->findParam("PARTN");
if (tmp) {
part_num = atoi(tmp);
} else {
SLOGW("Kernel block uevent missing 'PARTN'");
part_num = 1;
}
.........................
if (getState() != Volume::State_Formatting) {
setState(Volume::State_Idle);
snprintf(msg, sizeof(msg), "Volume %s %s Partition Added (%d:%d)",
getLabel(), getFuseMountpoint(), mDiskMajor, mDiskMinor);
mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,
msg, false);
}
void Volume::setState(int state) {
char msg[255];
int oldState = mState;
if (oldState == state) {
SLOGW("Duplicate state (%d)\n", state);
return;
}
if ((oldState == Volume::State_Pending) && (state != Volume::State_Idle)) {
mRetryMount = false;
}
mState = state;
SLOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel,
oldState, stateToStr(oldState), mState, stateToStr(mState));
snprintf(msg, sizeof(msg),
"Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(),
getFuseMountpoint(), oldState, stateToStr(oldState), mState,
stateToStr(mState));
mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
msg, false);
}
这里主要会发送两个广播到frameworks的MountService中去。
先在Volume::setState中通过mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,msg, false);发送一个VolumeStateChange类型的广播,代表挂载状态在改变,发送的信息为msg,msg的打印如下:
D/Vold ( 977): Javen.tang setState msg=Volume CAA5-CD0B /mnt/usb/sda1 state changed from 0 (No-Media) to 1 (Idle-Unmounted)
然后接着调用
mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,
msg, false);发送一个
VolumeDiskInserted类型的广播,代表U盘插入的意思,信息也是msg,打印msg信息可以看到:
D/DirectVolume( 977): Javen.tang msg=Volume CAA5-CD0B /mnt/usb/sda1 Partition Added (8:1)