Vold的学习过了一段时间了,才想着把mmcblock内置成sdcard的方法记录一下。
当初就是因为要做这个功能才去学习Vold机制,贴出之前的博客:
Android—— 4.2 Vold挂载管理_主体构建main (一)
Android—— 4.2 Vold挂载管理_CommandListener (二)
Android—— 4.2 Vold挂载管理_VolumeManager (三)
Android—— 4.2 Vold挂载管理_NetlinkManager (四)
Android—— 4.2 Vold挂载管理_DirectVolume/Volume (五)
Android—— 4.2 Vold挂载管理_MountService (六)
Android—— 4.2 Vold挂载管理_Kernel_USB_Uevent (七)
个人认为把上面的脉络理清了,Vold应该算是基本掌握了,其中的细节部分就需要另行研究了。
撰写不易,转载请注明出处:http://blog.youkuaiyun.com/jscese/article/details/39692953
一.准备:
不同平台的mmc分区方案和烧写emmc方式不一样,总之划分一块区域用来当sdcard挂载,并且记住block序号,android的分区类型在linux下制作一般都是ext4格式。
可在BoardCondif.mk中添加宏控制:
BOARD_MMCBLK_AS_SDCARD := 13
#jscese open mmcblk0p13 as sdcard to mount 140724#
并且在/system/vold/Android.mk 中添加编译选项:
ifneq ($(BOARD_MMCBLK_AS_SDCARD),)
common_cflags := -DMMCBLK_AS_SDCARD=$(BOARD_MMCBLK_AS_SDCARD)
endif
LOCAL_CFLAGS := $(common_cflags)
二.Vold挂载:
在/system/vold/VolumeManager.cpp 中的 handleBlockEvent 中添加特殊处理。
核心添加如下:
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;
major = atoi(evt->findParam("MAJOR"));
minor = atoi(evt->findParam("MINOR"));
if (major == 179) {
if (strncmp(dn,"mmcblk0",7) == 0) {//判断是否为mmc的block
#ifndef MMCBLK_AS_SDCARD
return;
#endif
}
if (strcmp(dn,"mmcblk0p13") == 0)
SLOGD("jscese display in vm - handle-isSDCard = true\n");
isSDCard = true;
}
snprintf(device,255,"/dev/block/vold/%d:%d",major,minor);
if (strcmp(devtype,"disk") == 0) { //根据设备类型就行判断
const char *nparts = evt->findParam("NPARTS");
if (nparts) {
int diskNumParts = atoi(nparts);
isRawDisk = (diskNumParts == 0);
} else {
return ;
}
} else {
const char *tmp = evt->findParam("PARTN");
if (tmp == NULL) {
return;
}
partIdx = atoi(tmp);
// if (strcmp(dn,"mmcblk0p14") == 0)
SLOGD("jscese display in vm - handle- isPartition = true\n");
isPartition = true;
}
以上是对从kernel发过来的event信息进行一些筛选,下面就需要对device以及Volume进行特殊处理:
if (isRawDisk || isPartition) {
//first find uuid from cache
UUIDCache::Entry *entry = uuidCache.searchEntry(device); /dev uuid缓存
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)) {//获取uuid
#ifdef NETLINK_DEBUG
SLOGD("can not get the uuid of %s when device add",device);
#endif
return;
}
}
//Volume容器中添加处理,针对上面的uuid
for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
if (strcmp(uuid, (*it)->getLabel()) == 0) {
(*it)->handleBlockEvent(evt);
hit = true;
if (evt->getAction() == NetlinkEvent::NlActionAdd) {
//if device was added at before, so cache its uuid and device path again
uuidCache.addEntry(device,uuid);
(*it)->setDevicePath(device);
}
...
}
}
}
if (!hit) {
static char index = 'a'-1;
char * mountPoint = NULL;
const char *dp = NULL;
DirectVolume * volume = NULL;
#ifdef NETLINK_DEBUG
SLOGW("No volumes handled block event for '%s'", devpath);
#endif
if (evt->getAction() != NetlinkEvent::NlActionAdd)
{
return;
}
dp = evt->findParam("DEVPATH");
if (dp == NULL) {
return;
}
SLOGD("jscese display in vm for 1 mVolumes and isSDCard ==%d, isPartition+=%d \n",isSDCard,isPartition);
/* Determine its mount point */
if (isSDCard) { //根据上面来的筛选
if (isRawDisk) {
asprintf(&mountPoint,"/mnt/sdcard_external");
} else if (isPartition) {
#ifdef MMCBLK_AS_SDCARD
if (partIdx == MMCBLK_AS_SDCARD) { //根据boardconfig中定义的block来设置挂载点
asprintf(&mountPoint,"/mnt/sdcard");
SLOGD("jscese display set mountpoint fuck mmcblock %d as sdcard\n",partIdx);
}
#else
if (partIdx == 1) { //外部的SD卡挂载点 改为 /mnt/sdcard_external
asprintf(&mountPoint,"/mnt/sdcard_external");
} else {
asprintf(&mountPoint,"/mnt/usb/%s",dn);
}
#endif
}
}
volume = new DirectVolume(this,uuid,mountPoint,partIdx);
//cache the device path of device
volume->setDevicePath(device);
free(mountPoint);
mVolumesLock.lock();
addVolume(volume);
mVolumesLock.unlock();
//cache the uuid of device
uuidCache.addEntry(device, uuid);
if ( volume->handleBlockEvent(evt) !=0 ) {
SLOGD("New add volume fail to handle the event of %s",devpath);
}
}
UUID的cache接口如下:
static bool getVolumeUUID(const char* path, char* buf, int size) {
blkid_cache cache = NULL;
blkid_dev dev = NULL;
blkid_tag_iterate iter = NULL;
const char *type = NULL, *value = NULL;
bool ret = false;
if (path == NULL || buf == NULL || size < 0) {
return false;
}
if (blkid_get_cache(&cache, NULL) < 0) {
return false;
}
dev = blkid_get_dev(cache, path, BLKID_DEV_NORMAL);
if (dev == NULL) {
return false;
}
iter = blkid_tag_iterate_begin(dev);
while (blkid_tag_next(iter, &type, &value) == 0) {
if (strcmp(type,"UUID") == 0) {
int n = strlen(value);
if (n > size) {
n = size;
}
ret = true;
strncpy(buf,value,n);
buf[n] = '\0';
}
}
blkid_tag_iterate_end(iter);
blkid_put_cache(cache);
return ret;
}
class UUIDCache {
public:
struct Entry {
char *device;
char *uuid;
};
UUIDCache() {
uuidCache = new EntryCollection();
}
~UUIDCache() {
delete uuidCache;
}
void addEntry(const char *device, const char *uuid) {
Entry *entry = NULL;
if (device == NULL || uuid == NULL) {
return;
}
entry = searchEntry(device);
if (entry == NULL) {
entry = new Entry();
entry->device = strdup(device);
uuidCache->push_back(entry);
} else {
if (entry->uuid != NULL) {
free(entry->uuid);
}
}
entry->uuid = strdup(uuid);
}
Entry *searchEntry(const char *device) {
EntryCollection::iterator it;
for (it = uuidCache->begin(); it != uuidCache->end(); ++it) {
if ((*it)->device != NULL && strcmp((*it)->device, device) == 0) {
return(*it);
}
}
return NULL;
}
private:
typedef android::List<Entry *> EntryCollection;
EntryCollection *uuidCache;
};
static UUIDCache uuidCache;
调入到DirectVolume中,不再做分析,前文有解析。
最后执行到挂载的Volume.cpp中的 mountVol 中。修改添加如下:
if (primaryStorage) {
// Special case the primary SD card.
// For this we grant write access to the SDCARD_RW group.
gid = AID_SDCARD_RW;
} else {
// For secondary external storage we keep things locked up.
gid = AID_MEDIA_RW;
}
SLOGE("jscese display in Volume-mountVol devicepath==%s , gid ==%d \n",devicePath,gid);
bool isFatFs = true;
bool isNtfsFS = true;
bool isExtFs = true;
if(isFatFs) {
SLOGE("jscese Fat::doMount \n");
if (Fat::doMount(devicePath, "/mnt/secure/staging", false, false, false,
AID_SYSTEM, gid, 0002, true)) {
SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
isFatFs = false;
} else {
isNtfsFS = false;
isExtFs = false;
}
}
/*===jscese add for format mmcblock ext4_type to vfat_type as sdcard 140805=====*/
#ifdef MMCBLK_AS_SDCARD
if(!isFatFs)
{
char checkmmcblokPath[255];
// sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]), MINOR(deviceNodes[i]));
sprintf(checkmmcblokPath, "/dev/block/vold/%d:%d", SD_MAJOR,MMCBLK_AS_SDCARD);
SLOGE("jscese Volume::mountvol format checkmmcblockpath= %s, devicepath==%s , mountpoint==%s\n",checkmmcblokPath,devicePath,getMountpoint());
if(!strcmp("/mnt/sdcard",getMountpoint()))
{
if(!strcmp(checkmmcblokPath,devicePath))
{
setState(Volume::State_Idle);
SLOGE("jscese Volume::mountvol format 1 label== %s\n",getLabel());
if(formatVol()==0)
{
setState(Volume::State_Checking);
SLOGE("jscese Volume::mountvol format over \n");
if (Fat::doMount(devicePath, "/mnt/secure/staging", false, false, false,
AID_SYSTEM, gid, 0002, true))
{
SLOGE("%s failed to mount via VFAT 2 (%s)\n", devicePath, strerror(errno));
isFatFs = false;
}
else
{
isFatFs=true;
isNtfsFS = false;
isExtFs = false;
}
}
}
}
}
#endif
/*===============end================*/
首先进行FAT形式挂载,在系统第一次运行时,因为sdcard的block为ext4格式,肯定是会false返回,这个时候在代码中将其 formatVol 格式化,再进行FAT形式挂载!
因为我发现,如果以ext4形式挂载的/mnt/sdcard 这时分区中文件的创建权限只有创建者 才能有可读写权限,并不是根据/mnt/sdcard来规定权限。
此时各个应用进程在/mnt/sdcard创建的文件夹,其它用户就无权访问了,失去了sdcard应有的价值!
所以这里要先格式化成FAT,之后的分区中的创建权限全又挂载点也就是/mnt/sdcard来规定!
上层框架不用改动,这样就可以实现mmc的一个block内置成sdcard供应用程序使用,同时外部插入的sdcard将被挂载到/mnt/sdcard_external下。