既上面两篇博客,继续分析vold、对外置SD卡和OTG的分析:

一、process_config函数

上一篇我们再main函数中分析了VolumeManager的start函数,这次我们接下来分析process_config函数

static int process_config(VolumeManager *vm) {std::string path(android::vold::DefaultFstabPath());fstab = fs_mgr_read_fstab(path.c_str());if (!fstab) {PLOG(ERROR) << "Failed to open default fstab " << path;return -1;}/* Loop through entries looking for ones that vold manages */bool has_adoptable = false;for (int i = 0; i < fstab->num_entries; i++) {if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {if (fs_mgr_is_nonremovable(&fstab->recs[i])) {LOG(WARNING) << "nonremovable no longer supported; ignoring volume";continue;}std::string sysPattern(fstab->recs[i].blk_device);std::string nickname(fstab->recs[i].label);int flags = 0;if (fs_mgr_is_encryptable(&fstab->recs[i])) {flags |= android::vold::Disk::Flags::kAdoptable;has_adoptable = true;}if (fs_mgr_is_noemulatedsd(&fstab->recs[i])|| property_get_bool("vold.debug.default_primary", false)) {flags |= android::vold::Disk::Flags::kDefaultPrimary;}PLOG(ERROR) << "process_config:" <<" sysPattern:" << sysPattern <<" nickname:" << nickname<<" end";vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(new VolumeManager::DiskSource(sysPattern, nickname, flags)));}}property_set("vold.has_adoptable", has_adoptable ? "1" : "0");return 0;
}

process_config函数就是遍历fstab文件,下面就是fstab文件

# Android fstab file.
#<src>                                         <mnt_point>  <type>  <mnt_flags and options>  <fs_mgr_flags>
# The filesystem that contains the filesystem checker binary (typically /system) cannot
# specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK/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,encryptable=false
/devices/a0400000.usb_hcd/usb1/*                                    auto      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

简单说上面的函数就是遍历fstab文件中有voldmanaged的那项,然后新建一个DiskSource对象,加入volumeManager。

我们再来看看上面加的log的打印

01-01 09:00:25.347   164   164 E vold    :process_config: sysPattern:/devices/platform/comip-mmc.0/mmc_host/mmc1/* nickname:sdcard1 end: Success
01-01 09:00:25.347   164   164 E vold    :process_config: sysPattern:/devices/a0400000.usb_hcd/usb1/* nickname:usbotg end: Success

一个nickname是sdcard1,另一个是usbotg。

二、Disk的创建

下面我们来看下kernel对sdcard的检测到之后,通知vold后,vold如何操作。

kernel收到插入sd或者otg的event后,先到NetlinkHandler的onEvent函数,这个在之前分析android5.1的时候分析过了,不再详述。

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函数,我们先看add的处理

void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {std::lock_guard<std::mutex> lock(mLock);if (mDebug) {LOG(VERBOSE) << "----------------";LOG(VERBOSE) << "handleBlockEvent with action " << (int) evt->getAction();evt->dump();}std::string eventPath(evt->findParam("DEVPATH"));std::string devType(evt->findParam("DEVTYPE"));if (devType != "disk") return;int major = atoi(evt->findParam("MAJOR"));int minor = atoi(evt->findParam("MINOR"));dev_t device = makedev(major, minor);switch (evt->getAction()) {case NetlinkEvent::Action::kAdd: {for (auto source : mDiskSources) {//把之前在process_config中加入的disksources的资源遍历,看是否有匹配的if (source->matches(eventPath)) {// For now, assume that MMC devices are SD, and that// everything else is USBint flags = source->getFlags();if (major == kMajorBlockMmc) {flags |= android::vold::Disk::Flags::kSd;} else {flags |= android::vold::Disk::Flags::kUsb;}auto disk = new android::vold::Disk(eventPath, device,source->getNickname(), flags);disk->create();mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk));break;}}break;}

遍历之前在process_config函数中放入VolumeManager的DiskSource,看看是否有匹配的。匹配的新建一个Disk对象,放入VolumeManager的mDisks中。并且调用disk的create函数:

status_t Disk::create() {CHECK(!mCreated);mCreated = true;notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));readMetadata();readPartitions();return OK;
}

在create函数中,先给MountService发送了DiskCreated的消息,我们看看MountService是如何处理的?

在MountService中在onEventLocked中对DiskCreated处理,也是放在mDisks变量中

    private boolean onEventLocked(int code, String raw, String[] cooked) {switch (code) {case VoldResponseCode.DISK_CREATED: {if (cooked.length != 3) break;final String id = cooked[1];int flags = Integer.parseInt(cooked[2]);if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)|| mForceAdoptable) {flags |= DiskInfo.FLAG_ADOPTABLE;}mDisks.put(id, new DiskInfo(id, flags));break;}

下面继续分析disk的create函数,看Disk::readMetadata函数

status_t Disk::readMetadata() {mSize = -1;mLabel.clear();int fd = open(mDevPath.c_str(), O_RDONLY | O_CLOEXEC);if (fd != -1) {if (ioctl(fd, BLKGETSIZE64, &mSize)) {mSize = -1;}close(fd);}switch (major(mDevice)) {case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC: case kMajorBlockScsiD:case kMajorBlockScsiE: case kMajorBlockScsiF: case kMajorBlockScsiG: case kMajorBlockScsiH:case kMajorBlockScsiI: case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:case kMajorBlockScsiM: case kMajorBlockScsiN: case kMajorBlockScsiO: case kMajorBlockScsiP: {std::string path(mSysPath + "/device/vendor");std::string tmp;if (!ReadFileToString(path, &tmp)) {PLOG(WARNING) << "Failed to read vendor from " << path;return -errno;}mLabel = tmp;break;}case kMajorBlockMmc: {std::string path(mSysPath + "/device/manfid");std::string tmp;if (!ReadFileToString(path, &tmp)) {PLOG(WARNING) << "Failed to read manufacturer from " << path;return -errno;}uint64_t manfid = strtoll(tmp.c_str(), nullptr, 16);// Our goal here is to give the user a meaningful label, ideally// matching whatever is silk-screened on the card.  To reduce// user confusion, this list doesn't contain white-label manfid.switch (manfid) {case 0x000003: mLabel = "SanDisk"; break;case 0x00001b: mLabel = "Samsung"; break;case 0x000028: mLabel = "Lexar"; break;case 0x000074: mLabel = "Transcend"; break;}break;}default: {LOG(WARNING) << "Unsupported block major type" << major(mDevice);return -ENOTSUP;}}notifyEvent(ResponseCode::DiskSizeChanged, StringPrintf("%" PRId64, mSize));notifyEvent(ResponseCode::DiskLabelChanged, mLabel);notifyEvent(ResponseCode::DiskSysPathChanged, mSysPath);return OK;
}

这个函数主要获取一些参数,然后发送给MountService,而MountService的处理如下,主要是把disk的参数修改下

            case VoldResponseCode.DISK_SIZE_CHANGED: {if (cooked.length != 3) break;final DiskInfo disk = mDisks.get(cooked[1]);if (disk != null) {disk.size = Long.parseLong(cooked[2]);}break;}case VoldResponseCode.DISK_LABEL_CHANGED: {final DiskInfo disk = mDisks.get(cooked[1]);if (disk != null) {final StringBuilder builder = new StringBuilder();for (int i = 2; i < cooked.length; i++) {builder.append(cooked[i]).append(' ');}disk.label = builder.toString().trim();}break;}case VoldResponseCode.DISK_SCANNED: {if (cooked.length != 2) break;final DiskInfo disk = mDisks.get(cooked[1]);if (disk != null) {onDiskScannedLocked(disk);}break;}case VoldResponseCode.DISK_SYS_PATH_CHANGED: {if (cooked.length != 3) break;final DiskInfo disk = mDisks.get(cooked[1]);if (disk != null) {disk.sysPath = cooked[2];}br

下面我们继续看Disk的readParttitions函数

status_t Disk::readPartitions() {int8_t maxMinors = getMaxMinors();if (maxMinors < 0) {return -ENOTSUP;}destroyAllVolumes();// Parse partition tablestd::vector<std::string> cmd;cmd.push_back(kSgdiskPath);cmd.push_back("--android-dump");cmd.push_back(mDevPath);std::vector<std::string> output;status_t res = ForkExecvp(cmd, output);if (res != OK) {LOG(WARNING) << "sgdisk failed to scan " << mDevPath;notifyEvent(ResponseCode::DiskScanned);mJustPartitioned = false;return res;}Table table = Table::kUnknown;bool foundParts = false;for (auto line : output) {char* cline = (char*) line.c_str();char* token = strtok(cline, kSgdiskToken);if (token == nullptr) continue;if (!strcmp(token, "DISK")) {const char* type = strtok(nullptr, kSgdiskToken);if (!strcmp(type, "mbr")) {table = Table::kMbr;} else if (!strcmp(type, "gpt")) {table = Table::kGpt;}} else if (!strcmp(token, "PART")) {foundParts = true;int i = strtol(strtok(nullptr, kSgdiskToken), nullptr, 10);if (i <= 0 || i > maxMinors) {LOG(WARNING) << mId << " is ignoring partition " << i<< " beyond max supported devices";continue;}dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);if (table == Table::kMbr) {const char* type = strtok(nullptr, kSgdiskToken);switch (strtol(type, nullptr, 16)) {case 0x06: // FAT16case 0x0b: // W95 FAT32 (LBA)case 0x0c: // W95 FAT32 (LBA)case 0x0e: // W95 FAT16 (LBA)createPublicVolume(partDevice);break;}} else if (table == Table::kGpt) {const char* typeGuid = strtok(nullptr, kSgdiskToken);const char* partGuid = strtok(nullptr, kSgdiskToken);if (!strcasecmp(typeGuid, kGptBasicData)) {createPublicVolume(partDevice);} else if (!strcasecmp(typeGuid, kGptAndroidExpand)) {createPrivateVolume(partDevice, partGuid);}}}}// Ugly last ditch effort, treat entire disk as partitionif (table == Table::kUnknown || !foundParts) {LOG(WARNING) << mId << " has unknown partition table; trying entire device";std::string fsType;std::string unused;if (ReadMetadataUntrusted(mDevPath, fsType, unused, unused) == OK) {createPublicVolume(mDevice);} else {LOG(WARNING) << mId << " failed to identify, giving up";}}notifyEvent(ResponseCode::DiskScanned);mJustPartitioned = false;return OK;
}

这个函数就主要会createPublicVolume或者createPrivateVolume,然后会通知MountService DiskScanned消息。先看上层对这个消息的处理。

            case VoldResponseCode.DISK_SCANNED: {if (cooked.length != 2) break;final DiskInfo disk = mDisks.get(cooked[1]);if (disk != null) {onDiskScannedLocked(disk);}break;}

找到DiskInfo后,调用了onDiskScannedLocked函数

    private void onDiskScannedLocked(DiskInfo disk) {int volumeCount = 0;for (int i = 0; i < mVolumes.size(); i++) {final VolumeInfo vol = mVolumes.valueAt(i);if (Objects.equals(disk.id, vol.getDiskId())) {volumeCount++;}}final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id);intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount);mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();final CountDownLatch latch = mDiskScanLatches.remove(disk.id);if (latch != null) {latch.countDown();}disk.volumeCount = volumeCount;mCallbacks.notifyDiskScanned(disk, volumeCount);}

这个函数中发送了广播,然后通知了回调。

三、CreatePublicVolume

接下来我们主要分析下createPublicVolume和createPrivateVolume两个函数。

先看createPublicVolume函数:

void Disk::createPublicVolume(dev_t device) {auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device));if (mJustPartitioned) {LOG(DEBUG) << "Device just partitioned; silently formatting";vol->setSilent(true);vol->create();vol->format("auto");vol->destroy();vol->setSilent(false);}mVolumes.push_back(vol);vol->setDiskId(getId());vol->create();
}

这个函数中,先新建了一个PublicVolume,然后让如了Disk的mVolumes中,最后调用了volume的create函数

status_t VolumeBase::create() {CHECK(!mCreated);mCreated = true;status_t res = doCreate();notifyEvent(ResponseCode::VolumeCreated,StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str()));setState(State::kUnmounted);return res;
}

create函数又回调之前分析过得,给上层发送VolumeCreated消息
只是这个create的doCreate函数在PublicVolume中有实现:

status_t PublicVolume::doCreate() {return CreateDeviceNode(mDevPath, mDevice);
}

然后往MountService发送VolumeCreated后,MountService会往vold发送mount。

status_t VolumeBase::mount() {if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {LOG(WARNING) << getId() << " mount requires state unmounted or unmountable";return -EBUSY;}setState(State::kChecking);status_t res = doMount();if (res == OK) {setState(State::kMounted);} else {setState(State::kUnmountable);}return res;
}

在Volumebase执行mount函数的时候,到doMount是一个虚函数会到PublicVolume的doMount函数

status_t PublicVolume::doMount() {// TODO: expand to support mounting other filesystemsreadMetadata();if (mFsType != "vfat") {LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;return -EIO;}if (vfat::Check(mDevPath)) {LOG(ERROR) << getId() << " failed filesystem check";return -EIO;}// Use UUID as stable name, if availablestd::string stableName = getId();if (!mFsUuid.empty()) {stableName = mFsUuid;}mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());//挂载地址,stableName为uuidmFuseDefault = StringPrintf("/mnt/runtime/default/%s", stableName.c_str());mFuseRead = StringPrintf("/mnt/runtime/read/%s", stableName.c_str());mFuseWrite = StringPrintf("/mnt/runtime/write/%s", stableName.c_str());setInternalPath(mRawPath);if (getMountFlags() & MountFlags::kVisible) {setPath(StringPrintf("/storage/%s", stableName.c_str()));} else {setPath(mRawPath);}if (fs_prepare_dir(mRawPath.c_str(), 0700, AID_ROOT, AID_ROOT) ||fs_prepare_dir(mFuseDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||fs_prepare_dir(mFuseRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT)) {PLOG(ERROR) << getId() << " failed to create mount points";return -errno;}if (vfat::Mount(mDevPath, mRawPath, false, false, false,//挂载sd卡AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {PLOG(ERROR) << getId() << " failed to mount " << mDevPath;return -EIO;}if (getMountFlags() & MountFlags::kPrimary) {initAsecStage();}if (!(getMountFlags() & MountFlags::kVisible)) {// Not visible to apps, so no need to spin up FUSEreturn OK;}dev_t before = GetDevice(mFuseWrite);if (!(mFusePid = fork())) {if (getMountFlags() & MountFlags::kPrimary) {//开启fuse文件系统,让storage下面的sd卡读取直接到mnt下挂载的地址if (execl(kFusePath, kFusePath,"-u", "1023", // AID_MEDIA_RW"-g", "1023", // AID_MEDIA_RW"-U", std::to_string(getMountUserId()).c_str(),"-w",mRawPath.c_str(),stableName.c_str(),NULL)) {PLOG(ERROR) << "Failed to exec";}} else {if (execl(kFusePath, kFusePath,"-u", "1023", // AID_MEDIA_RW"-g", "1023", // AID_MEDIA_RW"-U", std::to_string(getMountUserId()).c_str(),mRawPath.c_str(),stableName.c_str(),NULL)) {PLOG(ERROR) << "Failed to exec";}}LOG(ERROR) << "FUSE exiting";_exit(1);}if (mFusePid == -1) {PLOG(ERROR) << getId() << " failed to fork";return -errno;}while (before == GetDevice(mFuseWrite)) {LOG(VERBOSE) << "Waiting for FUSE to spin up...";usleep(50000); // 50ms}return OK;
}

这个函数我们先看readMetadata函数,会读取各个参数,然后发送到MountService中,在上层会把这些参数保存在DiskInfo中

status_t PublicVolume::readMetadata() {status_t res = ReadMetadataUntrusted(mDevPath, mFsType, mFsUuid, mFsLabel);notifyEvent(ResponseCode::VolumeFsTypeChanged, mFsType);notifyEvent(ResponseCode::VolumeFsUuidChanged, mFsUuid);notifyEvent(ResponseCode::VolumeFsLabelChanged, mFsLabel);return res;
}

再看下面这段代码,当uuid不为空,statbleName为uuid

    if (!mFsUuid.empty()) {stableName = mFsUuid;}

doMount就是把sd卡设备挂载到mnt/media_rw下面创建一个uuid的目录。然后在Storage下面也有一个uuid的目录,用fuse文件系统连接起来。大家可以看上面代码的注释。

四、createPrivateVolume

下面我们再来看Disk的createPrivateVolume方法

void Disk::createPrivateVolume(dev_t device, const std::string& partGuid) {std::string normalizedGuid;if (NormalizeHex(partGuid, normalizedGuid)) {LOG(WARNING) << "Invalid GUID " << partGuid;return;}std::string keyRaw;if (!ReadFileToString(BuildKeyPath(normalizedGuid), &keyRaw)) {PLOG(ERROR) << "Failed to load key for GUID " << normalizedGuid;return;}LOG(DEBUG) << "Found key for GUID " << normalizedGuid;auto vol = std::shared_ptr<VolumeBase>(new PrivateVolume(device, keyRaw));if (mJustPartitioned) {LOG(DEBUG) << "Device just partitioned; silently formatting";vol->setSilent(true);vol->create();vol->format("auto");vol->destroy();vol->setSilent(false);}mVolumes.push_back(vol);vol->setDiskId(getId());vol->setPartGuid(partGuid);vol->create();
}

和PublicVolume类似我们就直接看privateVolume的doMount函数

tatus_t PrivateVolume::doMount() {if (readMetadata()) {LOG(ERROR) << getId() << " failed to read metadata";return -EIO;}mPath = StringPrintf("/mnt/expand/%s", mFsUuid.c_str());//mount地址setPath(mPath);if (PrepareDir(mPath, 0700, AID_ROOT, AID_ROOT)) {PLOG(ERROR) << getId() << " failed to create mount point " << mPath;return -EIO;}if (mFsType == "ext4") {//fs类型int res = ext4::Check(mDmDevPath, mPath);if (res == 0 || res == 1) {LOG(DEBUG) << getId() << " passed filesystem check";} else {PLOG(ERROR) << getId() << " failed filesystem check";return -EIO;}if (ext4::Mount(mDmDevPath, mPath, false, false, true)) {PLOG(ERROR) << getId() << " failed to mount";return -EIO;}} else if (mFsType == "f2fs") {int res = f2fs::Check(mDmDevPath);if (res == 0) {LOG(DEBUG) << getId() << " passed filesystem check";} else {PLOG(ERROR) << getId() << " failed filesystem check";return -EIO;}if (f2fs::Mount(mDmDevPath, mPath)) {PLOG(ERROR) << getId() << " failed to mount";return -EIO;}} else {LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;return -EIO;}LOG(VERBOSE) << "Starting restorecon of " << mPath;// TODO: find a cleaner way of waiting for restorecon to finishproperty_set("selinux.restorecon_recursive", "");property_set("selinux.restorecon_recursive", mPath.c_str());char value[PROPERTY_VALUE_MAX];while (true) {property_get("selinux.restorecon_recursive", value, "");if (strcmp(mPath.c_str(), value) == 0) {break;}sleep(1);LOG(VERBOSE) << "Waiting for restorecon...";}LOG(VERBOSE) << "Finished restorecon of " << mPath;// Verify that common directories are ready to rollif (PrepareDir(mPath + "/app", 0771, AID_SYSTEM, AID_SYSTEM) ||//创建了各个目录PrepareDir(mPath + "/user", 0711, AID_SYSTEM, AID_SYSTEM) ||PrepareDir(mPath + "/media", 0770, AID_MEDIA_RW, AID_MEDIA_RW) ||PrepareDir(mPath + "/media/0", 0770, AID_MEDIA_RW, AID_MEDIA_RW) ||PrepareDir(mPath + "/local", 0751, AID_ROOT, AID_ROOT) ||PrepareDir(mPath + "/local/tmp", 0771, AID_SHELL, AID_SHELL)) {PLOG(ERROR) << getId() << " failed to prepare";return -EIO;}// Create a new emulated volume stacked above us, it will automatically// be destroyed during unmountstd::string mediaPath(mPath + "/media");//只把media传过去,fuse给storageauto vol = std::shared_ptr<VolumeBase>(new EmulatedVolume(mediaPath, mRawDevice, mFsUuid));addVolume(vol);vol->create();return OK;
}

当然最后完成后setState(kMounted)会通知MountService,上层只会调用onVolumeStateChangedLocked会发送广播并且通知回调。

这样整个vold主要的流程分析完了,当然这边没有分析fuse的相关内容,等以后再分析吧!



Android6.0 MountService和vold详解(三) vold SD卡、otg相关推荐

  1. ADI Blackfin DSP处理器-BF533的开发详解22:SD卡的设计和实现原理及应用(含源码)

    硬件准备 ADSP-EDU-BF533:BF533开发板 AD-HP530ICE:ADI DSP仿真器 软件准备 Visual DSP++软件 硬件链接 硬件设计原理图 功能介绍 ADSP-EDU-B ...

  2. ADI Blackfin DSP处理器-BF533的开发详解28:SD卡的文件系统(含源码)

    硬件准备 ADSP-EDU-BF533:BF533开发板 AD-HP530ICE:ADI DSP仿真器 软件准备 Visual DSP++软件 硬件链接 功能介绍 代码实现了通过文件系统读取 SD 卡 ...

  3. android sd卡名称,科普详解Android系统SD卡各类文件夹名称

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 15.moji:墨迹天气的缓存目录. 16.MusicFolders:poweramp产生的缓存文件夹. 17.openfeint:openfeint的缓 ...

  4. android sd卡列目录文件_(科普)详解Android系统SD卡各类文件夹名称

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 15.moji:墨迹天气的缓存目录. 16.MusicFolders:poweramp产生的缓存文件夹. 17.openfeint:openfeint的缓 ...

  5. android 华为sd卡路径,(科普)详解Android系统SD卡各类文件夹名称

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 系统数据类--系统文件存储相关文件夹 1..android_secure:官方app2sd的产物,存储了相关的软件使用认证验证,删除之后SD卡中的软件将无 ...

  6. 华为荣耀3c语言设置在哪个文件夹,(科普)详解Android系统SD卡各类文件夹名称...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 系统数据类--系统文件存储相关文件夹 1..android_secure:官方app2sd的产物,存储了相关的软件使用认证验证,删除之后SD卡中的软件将无 ...

  7. Android init.rc文件解析过程详解(三)

    Android init.rc文件解析过程详解(三) 三.相关结构体 1.listnode listnode结构体用于建立双向链表,这种结构广泛用于kernel代码中, android源代码中定义了l ...

  8. 关于android的4.2的0文件夹的详解

    关于android的4.2的0文件夹的详解 ---- android 4.0 ---- 在galaxy nexus(GN)手机上userdata分区很大,被挂在/data目录,用户的数据通常是放在sd ...

  9. linux 进程间通信 dbus-glib【实例】详解三 数据类型和dteeth(类型签名type域)(层级结构:服务Service --> Node(对象、object) 等 )(附代码)

    linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...

  10. Android Studio 插件开发详解三:翻译插件实战

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/78113868 本文出自[赵彦军的博客] 系列目录 Android Gradle使用 ...

最新文章

  1. golang sqlite数据库 rows.Close()造成 错误database is locked
  2. 040_Tooltip文字提示
  3. 手把手教你,Java如何实现二维码?【附源码】
  4. Qt 中pro文件换行注意的问题
  5. 安装 Windows 自动化 API 3.0 后,Visual Studio 2010 的运行速度更快
  6. RNN知识+LSTM知识+encoder-decoder+ctc+基于pytorch的crnn网络结构
  7. java遍历数组练习(for循环、foreach)
  8. Shell 07 项目案例
  9. 计算机系统中CPU的寄存器介绍
  10. vscode制表位_vscode 常用配置
  11. oracle add命令详解,oracle job详解
  12. c语言贪吃蛇 纯c,纯C语言贪吃蛇游戏
  13. 星环科技TDH基于Overlay网络架构为数据安全保驾护航
  14. 基于流量数据,我们深挖了这家史上增长最快的SaaS公司
  15. 关闭idea任务栏小图标
  16. 贪心 阿狸和桃子的游戏
  17. Elastic Stack核心技术实战01--Elasticsearch环境搭建与基础入门
  18. postgresql 并发访问_postgresql 并发update下导致的死锁问题
  19. 利用opencv进行图像处理,提取椭圆圆心处理
  20. python 根据 url 批量下载文件到本地

热门文章

  1. 服务器一直即将注销你的登录,Win10提醒即将注销你的登录怎么办?
  2. Linux下BMP图片截图
  3. js 定时器的开启与关闭
  4. mysql下载安装(简单)
  5. 63 Three.js 将多个网格合并成一个网格
  6. 秋招斩获所有互联网大厂面经之前端
  7. 光学基础知识:焦点、弥散圆、景深、焦深
  8. 如何用Python判断水仙花数
  9. element ui框架(准备)
  10. sprintf, snprintf, _snprintf, sprintf_s 等的区别