既上面两篇博客,继续分析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:" <

vm->addDiskSource(std::shared_ptr<: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.

#

# 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<: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 USB

int 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<: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 table

std::vector<:string> cmd;

cmd.push_back(kSgdiskPath);

cmd.push_back("--android-dump");

cmd.push_back(mDevPath);

std::vector<: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: // FAT16

case 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 partition

if (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(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 filesystems

readMetadata();

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 available

std::string stableName = getId();

if (!mFsUuid.empty()) {

stableName = mFsUuid;

}

mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());//挂载地址,stableName为uuid

mFuseDefault = 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 FUSE

return 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(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 finish

property_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 roll

if (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 unmount

std::string mediaPath(mPath + "/media");//只把media传过去,fuse给storage

auto vol = std::shared_ptr(

new EmulatedVolume(mediaPath, mRawDevice, mFsUuid));

addVolume(vol);

vol->create();

return OK;

}

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

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



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

  1. android下载zip到assets,Android将assets中的zip压缩文件解压到SD卡

    程序首先要获取写外部存储权限: 程序: package com.hu.andstar; import java.io.File; import java.io.FileOutputStream; im ...

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

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

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

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

  4. android ble蓝牙接收不到数据_Android蓝牙4.0 Ble读写数据详解 -2

    Android蓝牙4.0 Ble读写数据详解 -2 上一篇说了如何扫描与链接蓝牙 这篇文章讲讲与蓝牙的数据传输,与一些踩到的坑. 先介绍一款调试工具,专门调试Ble蓝牙的app.名字叫:nRF-Con ...

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

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

  6. android 4.2目录结构,关于android的4.2的0文件夹的详解(目录结构挂载分析)

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

  7. android中怎么网络判断,Android中判断网络是否连接实例详解

    Android中判断网络是否连接实例详解 在android中,如何监测网络的状态呢,这个有的时候也是十分重要的,方法如下: public class ConnectionDetector { priv ...

  8. 【转】Android APK反编译就这么简单 详解(附图)

     转自:http://blog.csdn.net/vipzjyno1/article/details/21039349/ [置顶] Android APK反编译就这么简单 详解(附图) 分类: and ...

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

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

最新文章

  1. HTTP头部信息解释分析(详细整理)
  2. 一文读懂工业物联网 全面起底核心玩家和技术体系
  3. 清华「计图」迎来重大更新:支持热门的可微渲染,多项CV任务速度超越PyTorch...
  4. 深拷贝与浅拷贝、空类与空数组
  5. ChildTuning:试试把Dropout加到梯度上去?
  6. 滚动插件jQuery Marquee
  7. 【LeetCode】28. Implement strStr()
  8. 关于十字翻转棋的解法研究
  9. 搞定 office 2007 错误 1706
  10. Github Actions生成 secrets
  11. 苹果手机如何深度清理_安卓手机必备清理软件APP,完全免费超级深度清理
  12. python中线条颜色_python – 在pandas中指定线条颜色
  13. 小白学python(爬虫知识1(大体框架)
  14. vue项目上传图片的方法
  15. 智能小区安防子系统实现
  16. 下载软件-我爱分享网
  17. 深入探究JVM(1) - Java的内存区域解析
  18. 基于Python实现的新闻网络爬虫程序(附完整代码)
  19. 【汇智学堂】-python小游戏(生成.exe文件)
  20. c#打包安装程序默认安装路径设置

热门文章

  1. 淘宝直播的“一哥”和“一姐”:当年有多卑微,今天就有多荣耀!
  2. 【python基础语法十】面向对象
  3. 清华大学(软件学院)-用友网络科技股份有限公司时序数据与物联应用联合研究中心成立...
  4. 微信小游戏审核不通过解决方案(小游戏需具有完整的游戏玩法,不能为简单的素材堆砌)
  5. Java API访问ZK的权限控制
  6. 一个串口接2个设备_姑娘2个月相亲28次!崩溃大喊:我像一个没有感情的机器...
  7. :数字电路智能循迹小车
  8. 企业采购如何才能更高效的进行?
  9. 计算机数据库英语词汇,计算机英语词汇:数据库
  10. 黄金点游戏(结对编程项目)