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.csdn.net/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下。