【存储模块培训文档,很认真写的,现在转移到博客上】
Android 存储模块解析
1.存储系统架构
Android 的存储系统主要由SystemServer进程中的MountService和Vold进程中的VolumeManager组成,它们管理着系统的存储设备,执行各种操作,包括 卸载(unmount)、挂载(mount)、格式化(format)等。
存储系统的架构如下图所示:
在Android的存储系统中,MountService是为应用提供服务的Binder类,运行在SystemServer中,StorageManager是MountService的代理,在用户进程中被使用。Vold是一个守护进程,负责和底层存储系统的驱动交互。MountService和Vold之间通过socket进行双向通信:MountService向vold下发命令,vold向MountService反馈底层硬件发生的变化。
Vold进程的主体是VolumeManager对象,它管理着系统底层所有的Disk和Volume对象,实现存储的各种操作。Vold中的CommandListener对象负责和MountService中的NativeDemonConnector对象进行Socket通信,NetlinkHandler对象负责坚挺kernel层上报的Netlink Socket消息。
- Vold守护进程
Vold 是 Volume Daemon的缩写,主要用来管理Android系统中的:1.USB存储设备;2.SD卡设备。
2.1 vold 初始化
Vold通过init进程启动,它在init.rc中定义:
service vold /system/bin/vold \
--blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \
--fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0
class core
socket vold stream 0660 root mount
socket cryptd stream 0660 root mount[p1]
ioprio be 2
//blkid_context 等为linxu下blkid 和 fsck两个工具的运行环境,这里主要的规定的是运行时的selinux 环境。具体见注1.
Vold服务属于core组,系统启动时会被init进程启动。此外,这里还定义了2个socket:
1)socket vold:用于vold和JAVA层的MountService通信
Vold的源码路径为:/system/vold , 入口函数main():
int main(int argc, char** argv){
... ...
VolumeManager *vm;
CommandListener *cl;
CryptCommandListener *ccl;
NetlinkManager *nm;
//检测vold运行时的selinux环境,具体见注1
parse_args(argc, argv);
sehandle = selinux_android_file_context_handle();
if (sehandle) {
selinux_android_set_sehandle(sehandle);
}
//初始化在init.rc中定义的vold和cryptd两个socket。
// Quickly throw a CLOEXEC on the socket we just inherited from init
fcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC);
fcntl(android_get_control_socket("cryptd"), F_SETFD, FD_CLOEXEC);
//创建 “/dev/block/vold”这个文件夹,此文件夹下记录着vold守护进程中创建的Disk对象和volume(包括public 和 private)对象
mkdir("/dev/block/vold", 0755);
/* For when cryptfs checks and mounts an encrypted filesystem */
klog_set_level(6);[p3]
//初始化VolumeManager 和NetlinkManager对象
/* Create our singleton managers */
if (!(vm = VolumeManager::Instance())) {
... ...
}
if (!(nm = NetlinkManager::Instance())) {
... ...
}
if (property_get_bool("vold.debug", false)) {
vm->setDebug(true);
}
//CommandListener负责和JAVA层的NativeDeamonConnector的通信,CryptCommandListener负责[p4]
cl = new CommandListener();
ccl = new CryptCommandListener();
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
if (vm->start()) {
... ...
}
if (process_config(vm)) {
... ...
}
if (nm->start()) {
... ...
}
coldboot("/sys/block");
... ...
//进入循环睡眠
exit(0);
}
注1:
parse_args这个方法根据init.rc中配置的blkid_context 和fsck_context等信息初始化了android::vold::sBlkidContext / sBlkidUntrustedContext / sFsckContext / sFsckUntrustedContext等变量,这些变量影响到了后续vold流程中调用blkid和fsck等工具时,调用/system/vold/Utils::ForkExecvp 中setexeccon(context) 的结果:
如果下发BlkidContext,就要求vold处于selinux 已初始化且为enable状态,否则运行blkid工具时会导致vold进程被强制abort。
BlkidUntrustedContext则对selinux环境无限制。
2.2 process_config
process_config函数首先是读取fstab.${ro.hardware}文件的内容,然后根据文件中的每行定义的存储器的属性值来调用VolumeManager对象中的方法来创建Disk对象,并规划到VolumeManager的管理中。
static int process_config(VolumeManager *vm) {
... ...
bool has_adoptable = false;
... ...
//读取fstab文件,筛选出<fs_mgr_flags>中有标记voldmanaged
if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
//不支持标有nonremovable的。前面已经说了,vold仅负责管理“可移动”的存 储设备
//初始化DiskSource::sysPattern,被赋值于fstab文件中的<sys_path>
std::string sysPattern(fstab->recs[i].blk_device);
//初始化DiskSource::nickname,被赋值于fstab文件的中的<fs_mgr_flags>中的flag_vals.label;
std::string nickname(fstab->recs[i].label);
int flags = 0;
[p5]
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;
}
vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(
new VolumeManager::DiskSource(sysPattern, nickname, flags)));
... ...
property_set("vold.has_adoptable", has_adoptable ? "1" : "0");
return 0;
}
2.3 fstab文件
Android系统上由于使用了Vold取代了udev来管理磁盘,自然也要有一个类似于udev的config来配置和管理磁盘的挂载,Android使用的是vold.fstab来实现这一目的。该配置文件的格式和解析意义如下表:
格式 |
说明 |
示例 |
<sys_path> |
设备实际路径 |
/devices/platform/comip-mmc.0/mmc_host/mmc1 |
<mnt_point> |
挂载点 |
storage/sdcard1 |
<type> |
挂载格式 |
dev_mount |
<mnt_flags and options> |
|
|
<fs_mgr_flags> |
分区的属性,包括是否加密、是否可以移动(物理卸载) |
|
需要注意的是:
//TODO 解析fstab文件的代码
在L1860项目上,对应的配置文件为:/device/leadcore/lte26007/fstab.lc1860
//TODO 具体到18601项目上fstab文件的特殊性
2.4 VolumeManager
VolumeManager的主要作用就是创建并管理Disk和volume对象,并处理又kernel层上报的block_event消息以及由java层下发的对Disk 和 volume等对象的mount,unmount,format等操作。
2.4.1 start
VolumeManager的构造函数没什么可以说的,VolumeManager的对象是在main()中创建的,创建后即调用vm->start();
int VolumeManager::start() {
//每次start VolumeManager之前均unmount所有VolumeManager管理的volume对象
unmountAll();
//创建mInternalEmulated对象。这个mInternalEmulated对象就代表我们经常说的“内部存储”或者“内置SD卡”
CHECK(mInternalEmulated == nullptr);
mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(
new android::vold::EmulatedVolume("/data/media"));
mInternalEmulated->create();
... ...
关于EmulatedVolume 对象的具体信息,在后面分析。
有上述可以发现,在VolumeManager初始化时,仅仅只是初始化了mInternalEmulated这个volume,没有外部存储设备相关的volume信息:Vold默认仅仅只有内部存储一定是存在的。
2.4.2 handleBlockEvent
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
std::lock_guard<std::mutex> lock(mLock);
std::string eventPath(evt->findParam("DEVPATH"));
std::string devType(evt->findParam("DEVTYPE"));
//仅仅只处理disk的消息
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) {
if (source->matches(eventPath)) {
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<android::vold::Disk>(disk));
break;
}
}
break;
}
case NetlinkEvent::Action::kChange: {
LOG(DEBUG) << "Disk at " << major << ":" << minor << " changed";
for (auto disk : mDisks) {
if (disk->getDevice() == device) {
disk->readMetadata();
disk->readPartitions();
}
}
break;
}
case NetlinkEvent::Action::kRemove: {
auto i = mDisks.begin();
while (i != mDisks.end()) {
if ((*i)->getDevice() == device) {
(*i)->destroy();
i = mDisks.erase(i);
} else {
++i;
}
}
break;
}
default: {
LOG(WARNING) << "Unexpected block event action " << (int) evt->getAction();
break;
}
}
}
HandleBlockEvent 是NetlinkHandler在接收到kernel上报的block_event后调用的,NetlinkHandler在后面会进行详细的分析。
● handleBlockEvent 会分析上报event的device的major num 和minor num,通过调用makedev来生成对应的device。以L1860位例,evt中上报的device num 为45952,转化为2进制为 1011001110000000,取后8位为minor,其余的前8位为major,则major =179,minor =129,所以device为”179,129”。
●从kernel层发送的消息分为3大类:NLActionAdd,NLActionRemove,NLActionChange.分别对应
1.NetlinkEvent::Action::kAdd
handleBlockEvent 会分析上报的event中eventPath的值,这个值代表event事件中的设备所处于kernel层的<sys_path> 。当这个值和我们在vold的main:main()中调用process_config 读取fstab.${ro.hardware}生成的DiskSource对象的<sys_path> 相同时,handleBlockEvent 才会继续处理这个event。
AndroidFramework层原生是适配SD卡的,但是有些时候我们发现,即使kernel已经能正常的识别SD设备,Framework层仍然不会去挂载SD卡。原因就在于kernel上报的这个event中的eventPath和我们在fstab.${ro.hardware}中配置的<sys_path>不匹配,HandleBlockEvent 主动跳过了这个event。
因此,我们在确定fstab.${ro.hardware}文件时中<sys_path>的值时,需要根据kernel上报的event中EventPath的值来确定。
以lc1860为例:/dev/block/mmcblk1p1和/devices/platform/comip-mmc.0/mmc_host/mmc1/*均可以代表SD卡的path,但是kernel的event上报的EventPath的为“”[p6]
在NetlinkEvent::Action::kAdd中,VolumeManager主要做了两件事:
- 通过上报device 的major num判断设备的类型,major =179的确定为sdcard设备,将此设备对应的Disk对象的flags中添加android::vold::Disk::Flags::kSd位;其余的均为USB存储设备,在其对应的Disk对象的flags中添加 android::vold::Disk::Flags::kUsb。
对于Android的存储系统来说,Sdcard 和USB 存储设备是两种不同的存储设备,两者的挂载流程,挂载路径,挂载后的权限以及对两设备中文件的读写权限的限制都会不同,大部分时候都是通过Disk::Flags 来进行区分的。
- 创建disk 对象,强disk添加到mDisks,纳入VolumeManager的管理中
- NetlinkEvent::Action::kChange
3.NetlinkEvent::Action::kRemove
tip 1 device的major和minor number
Linux系的/dev目录下面的的设备文件是用来表示外设的,如/dev/sda1表示第一块硬盘的第一个分区。但是这个/dev/sda1仅仅是方便用户观察,linux内核中表示不同的设备是通过major 和minor number实现的,通过major和minor Number来加载相应的驱动程序。
major number:表示不同的设备类型,主设备号
minor number:表示同一个设备的的不同分区,次设备号
在 Linux 2.6 内核中,表示 major 和 minor 的数据结构是一个32bit的字段 dev_t type (defined in <linux/types.h>),表示major 和 minor 的位数分别是 12 和 20 bits。
2.5 Disk 对象
Disk对象定义在/system/vold/disk.cpp中,构造函数如下:
Disk::Disk(const std::string& eventPath, dev_t device,
const std::string& nickname, int flags) :
mDevice(device), mSize(-1), mNickname(nickname), mFlags(flags), mCreated(
false), mJustPartitioned(false) {
mId = StringPrintf("disk:%u,%u", major(device), minor(device));
mEventPath = eventPath;
mSysPath [p9] = StringPrintf("/sys/%s", eventPath.c_str());
mDevPath = StringPrintf("/dev/block/vold/%s", mId.c_str());
CreateDeviceNode(mDevPath, mDevice);
}
其中:
mEventPath /eventPath:disk对象对应设备在kernel层的实际路径;
mDevPath :在lc1860中为Disk:179,129。Vold层管理的设备记录在/dev/block/vold文件下
device:disk对象对应设备的实际设备号,在lc1860中为179,129
nickname:disk对象对应设备的标志;
flags:disk对象的flags,具体定义在Disk.h中:
enum Flags {
/* Flag that disk is adoptable */
kAdoptable = 1 << 0,
/* Flag that disk is considered primary when the user hasn't
* explicitly picked a primary storage location */
kDefaultPrimary = 1 << 1,
/* Flag that disk is SD card */
kSd = 1 << 2,
/* Flag that disk is USB disk */
kUsb = 1 << 3,
/* Flag that disk is EMMC internal */
kEmmc = 1 << 4,
};
2.5.1 CreateDeviceNode
定义在/system/vold/Utils.cpp下,主要的工作是调用了 mknod(cpath, mode, dev) 函数。其中:
cpath:为Disk::mDevPath,在1860项目中为 /dev/block/vold/Disk:179,129;
dev:为Disk::mDevice,在1860中为179,129这个device;
mknod 命令是建立一个目录项和一个特殊文件的对应索引节点,CreateDeviceNode函数的作用就是将dev/block/vold/Disk:179,129这个文件夹作为了179,129 这个设备的索引点,之前在2.4 VolumeManager中说明,179,129这个device实际代表kernel层中外置SD卡的对应的文件夹,因此,现在/dev/block/vold/Disk:179,129就代表了外置SD卡的文件夹索引点[p10] 。
2.5.2 Disk::readMetadata()
元数据(Metadata),又称中介数据、中继数据,为描述数据的数据(data about data),主要是描述数据属性(property)的信息,用来支持如指示存储位置、历史数据、资源查找、文件记录等功能。元数据算是一种电子式目录,为了达到编制目录的目的,必须在描述并收藏数据的内容或特色,进而达成协助数据检索的目的。
具体函数如下:
status_t Disk::readMetadata() {
mSize = -1;
mLabel.clear();
... ...
switch (major(mDevice)) {
case kMajorBlockScsiA: case kMajorBlockScsiB:case ... ...
//插入SD卡或者USB存储设备时都不会满足条件,忽略
... ...: {
... .. ..
}
//kMajorBlockMmc = “179”,代表设备属于mmc存储设备
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);[p11]
switch (manfid) {
case 0x000003: mLabel = "SanDisk"; break;
case 0x00001b: mLabel = "Samsung"; break;
case 0x000028: mLabel = "Lexar"; break;
case 0x000074: mLabel = "Transcend"; break;
}
break;
}
... ...
}
//向java层发送DiskSizeChanged,DiskLabelChanged,DiskSysPathChanged的Event
notifyEvent(ResponseCode::DiskSizeChanged, StringPrintf("%" PRId64, mSize));
notifyEvent(ResponseCode::DiskLabelChanged, mLabel);
notifyEvent(ResponseCode::DiskSysPathChanged, mSysPath);
return OK;
}
简单来说,Disk::readMetadata函数,主要就是为了检查Disk对象所对应的存储设备的各种属性信息:
Dis::mLabel : 对应设备的manfid,即生产厂商;
Disk::mSize : disk的代表的存储设备的总容量[p12]
Disk::mSysPath : Device path under sysfs
并通知JAVA层的MountService更新对应Disk的相应属性值。
2.5.3 Disk::readPartitions()
//TODO code explain
status_t Disk::readPartitions() {
int8_t maxMinors = getMaxMinors();[p13]
if (maxMinors < 0) {
return -ENOTSUP;
}
// read代表重新读取Disk中个partition信息并生产信息的volume对象。所以需要destroy all。
destroyAllVolumes();
//1860中,下发的命令为/system/bin/blkid --android-dump /dev/block/vold/Disk:179,128”
std::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);
... ...
//解析返回的结果
for (auto line : output) {
... ...
if (!strcmp(token, "DISK")) {
//解析disk信息,确定记录Disk的文件系统的是MBR表还是gpt表
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")) {
//解析Disk下的partition信息
foundParts = true;
int i = strtol(strtok(nullptr, kSgdiskToken), nullptr, 10);
... ...
//根据的disk的Major_num和Minor_num 来确定其中个partition的 device_num。Major_num代表设备的类型,不变;Minor_num 由disk的Minor_num加此分区的分区num
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);
}
... ...
}
readPartition()的主要作用是调用linux原生的blkid工具,下发
“/system/bin/blkid --android-dump /dev/block/vold/Disk:179,128”命令,读取disk对应的存储设备的partition分区信息,根据反馈的信息,决定创建PublicVolume对象还是PrivateVolume对象。
2.5.4 Disk::partitionPublic()
status_t Disk::partitionPublic() {
... ...
//清楚当前disk下所有的volume对象
destroyAllVolumes();
... ...
//对disk设备下发 /system/bin/sgdisk --zap-all /dev/block/vold/disk:179,128命令
//擦出disk设备的分区表
std::vector<std::string> cmd;
cmd.push_back(kSgdiskPath);
cmd.push_back("--zap-all");
cmd.push_back(mDevPath);
... ...
if ((res = ForkExecvp(cmd)) != 0) {
LOG(WARNING) << "Failed to zap; status " << res;
}
//更新diskinfo
int rc = apply_disk_config(&dinfo, 0);
//
return rc;
}
2.5.5 Disk::partitionPrivate()
代码较多,其实就是对diks的存储设备下发了一系列命令,已1860为例,下发了:
/system/bin/sgdisk
--new=0:0:+16M
--typecode=0:19A710A2-B3CA-11E4-B026-10604B889DCF
--change-name=0:android_meta
--new=0:0:-0
--typecode=0:193D1EA4-B3CA-11E4-B075-10604B889DCF
--partition-guid=0:aae61afb6d099da2a04cf52ce5b5d01a
--change-name=0:android_expand
/dev/block/vold/disk:179,128
此命令的作用就是将/dev/block/vold/disk:179,128 对应的存储设备分为2个partitions,其中
1.一个应该为PART 1,地址为 179:129(这个partition应该暂时无用,作为后续扩展什么的用途);
Part1 的注释:
// Define a metadata partition which is designed for future use; there
// should only be one of these per physical device, even if there are
// multiple private volumes.
- 为PART2,地址为179:130.用于作为存储卡用途的partition
实际下发的命令结果:
2.6 volume对象
2.6.1 VolumeBase
1.构造函数:
VolumeBase::VolumeBase(Type type) :
mType(type), mMountFlags(0), mMountUserId(-1), mCreated(false), mState(
State::kUnmounted), mSilent(false) {
}
2.具体成员
enum class Type {
//PublciVolume,公共存储vol,对于与外置SD卡
kPublic = 0,
//PrivateVolume,私有存储vol,对应于adoptable devices volumes
kPrivate,
//EmulatedVolume,内部存储vol,对应于/data/media
kEmulated,
//
kAsec,
//
kObb,
};
private:
/* ID that uniquely references volume while alive */
//volume对应的ID,一般是根据Disk的ID 生成,与disk的major_num相同,代表属于同种类型是存储设备,minor_num是在Disk的minor_num基础上计算得出,一般Disk分化为几个分区,第N个分区对应的minor_num = disk_minor_num + N,具体见
std::string mId;
/* ID that uniquely references parent disk while alive */
//生成volume的Disk的ID
std::string mDiskId;
/* Partition GUID of this volume */
std::string mPartGuid;
/* Volume type */
Type mType;
/* Flags used when mounting this volume */
int mMountFlags;
/* User that owns this volume, otherwise -1 */
//多用户状态下会涉及到,每个Volume对于与特定的user
userid_t mMountUserId;
/* Flag indicating object is created */
bool mCreated;
/* Current state of volume */
State mState;
/* Path to mounted volume */
// volume 实际挂载路径,对于public来说,就是"storge" + Fs_UUID
std::string mPath;
/* Path to internal backing storage */
std::string mInternalPath;
/* Flag indicating that volume should emit no events */
bool mSilent;
/* Volumes stacked on top of this volume */
std::list<std::shared_ptr<VolumeBase>> mVolumes;
void setState(State state);
DISALLOW_COPY_AND_ASSIGN(VolumeBase);
};
2.6.1.1 volume状态
enum class State {
//被卸载状态
kUnmounted = 0,
//正处于被检查状态,一般处于此状态时,代表volume正处于被fsck检查文件系统的状态,fsck会根据Volume对于的Disk所对应的emmc设备的MBR表检测emmc设备是否正常,然后检查emmc设备的文件系统是否正常,如果以上出现问题,会先尝试修复,修复失败后才返回-1
kChecking,
//挂载状态
kMounted,
//挂载状态,且只能读,不可写
kMountedReadOnly,
//正处于被格式化状态
kFormatting,
//
kEjecting,
//无法挂载状态
kUnmountable,
//emmc设备被移除状态,一般是接受到底层emmc驱动上报的消息后才会处于此种状态
kRemoved,
//非法移除状态,USB OTG热插拔
kBadRemoval,
};
2.6.2 PublicVolume对象
由Disk::readPatitions() 中调用Disk::createPublicVolume()创建。公共访问存储区,对应于外置SD卡,USB OTG。
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();
}
2.6.3 PrivateVolume对象
由Disk::readPatitions() 中调用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();
}
2.6.4 EmulatedVolume对象
在VolumeManager中创建:
mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(
new android::vold::EmulatedVolume("/data/media"));
mInternalEmulated->create();
- 消息的传递
3.1 监听驱动发出的消息
3.1.1 NetlinkManager对象
NetLinkManager对象的主要作用是监听驱动发出的uevent消息。main()函数中调用NetlinkManager类的静态函数Instance()里创建NetlinkManager对象:
/system/vold/NetlinkManager.java
NetlinkManager *NetlinkManager::Instance() {
if (!sInstance)
sInstance = new NetlinkManager();
return sInstance;
}
Istance()函数创建了NetlinkManager对象并通过静态变量sInstance来应用,这就意味着vold进程中只会有一个NetlinkManager对象。
构造函数:
NetlinkManager::NetlinkManager() {
mBroadcaster = NULL;
}
NetlinkManager的构造函数只是对变量mBroadcaster进行初始化。main()函数中会调用NetlinkManager的setBroadcaster()函数给变量mBroadcaster重新赋值:
int main(int argc, char** argv) {
... ...
nm->setBroadcaster((SocketListener *) cl);
... ...
if (nm->start()){
... ...
}
/system/vold/NetlinkManager.cpp
int NetlinkManager::start() {
struct sockaddr_nl nladdr;
//设置64K 的缓冲区
int sz = 64 * 1024;
int on = 1;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = getpid();
nladdr.nl_groups = 0xffffffff;
if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,
NETLINK_KOBJECT_UEVENT)) < 0) {
... ...
}
if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
... ...
}
if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
... ...
}
if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
... ...
}
mHandler = new NetlinkHandler(mSock);
if (mHandler->start()) {
... ...
}
其中:NETLINK_KOBJECT_UEVENT代表kernel的内核消息的Uevent事件。
3.1.2 NetlinkHandler
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
VolumeManager *vm = VolumeManager::Instance();
const char *subsys = evt->getSubsystem();
... ...
if (!strcmp(subsys, "block")) {
vm->handleBlockEvent(evt);
}
}
仅仅只会监听“block”类型的uevent事件。这个uevent事件会下发到VolumeManager::handleBlockEvent中进行处理。
3.1.* socket
int socket(int domain, int type, int protocol);
第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置AF_INET;
第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM、原始套接字SOCK_RAW(WinSock接口并不适用某种特定的协议去封装它,而是由程序自行处理数据包以及协议首部);
两个重要的类型是 SOCK_STREAM 和 SOCK_DGRAM。 SOCK_STREAM表明数据象字符流 一样通过 socket 。而 SOCK_DGRAM 则表明数据将是数据报(datagrams)的形式。
3.2 处理MountService发送的消息
MountService和vold之间是通过CommandListener对象进行通信的,在main函数中,对其进行初始化:
/system/vold/main.cpp
main() {
... ...
cl = new CommandListener();
vm->setBroadcaster((SocketListener *) cl);
... ...
if (cl->startListener()) {
PLOG(ERROR) << "Unable to start CommandListener";
exit(1);
}
... ...
}
cl是CommandListener类实例化的一个对象,该对象负责和Framework进行通信。
CommandListener—>FrameworkListener—>SocketLisenter
3.2.1 CommandListener
在CommandLisenter中,定义了6个类,这6个类都继承了VoldCommand类,VoldCommand类继承了FrameworkCommand类:
VoldCommand—>FrameworkCommand
class CommandListener : public FrameworkListener {
public:
CommandListener();
virtual ~CommandListener() {}
private:
static void dumpArgs(int argc, char **argv, int argObscure);
static int sendGenericOkFail(SocketClient *cli, int cond);
class DumpCmd : public VoldCommand {
... ...
class VolumeCmd : public VoldCommand {
... ...
class AsecCmd : public VoldCommand {
... ...
class ObbCmd : public VoldCommand {
... ...
class StorageCmd : public VoldCommand {
... ...
class FstrimCmd : public VoldCommand {
... ...
};
在这个类中,VoldCommand主要写了一个纯虚函数runCommand,声明如下:
virtual int runCommand(SocketClient *c, int argc, char **argv) = 0;
为了就是在CommandListener类中的实现,这里总共实现了6个版本的runCommand函数:
CommandLisenter在/system/vold/main.cpp::main()中被实例化,然后调用 cl->startListener()
我们现在讲startListener函数:
/system/core/libsysutils/src/SocketListenercpp :: startListener
int SocketListener::startListener() {
return startListener(4);
}
int SocketListener::startListener(int backlog) {
... ...
//初始化很多变量,其中传入的backlog = 4,代表sock可以同时处理的最大连接请求数量为4。
if (mListen && listen(mSock, backlog) < 0){
... ...
//将此socket添加到client端
mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
... ...
if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
... ...
}
threadStart最后调用runListener启动SocketListener:
void SocketListener::runListener() {
While(1) {
... ...
for (it = mClients->begin(); it != mClients->end(); ++it) {
SocketClient* c = *it;
int fd = c->getSocket();
if (FD_ISSET(fd, &read_fds)) {
pendingList.push_back(c);
c->incRef();
}
}
... ...
while (!pendingList.empty()) {
/* Pop the first item from the list */
it = pendingList.begin();
SocketClient* c = *it;
pendingList.erase(it);
/* Process it, if false is returned, remove from list */
if (!onDataAvailable(c)) {
release(c, false);
}
c->decRef();
}
... ...
}
}
runListener 会在一个死循环环中一直遍历mClients队列中的socket对象,调用onDaraAvailable()函数来处理。onDataAvailable函数是类的纯虚函数,在FrameworkLisenter类中实现了该函数:
/system/core/libsysutils/src/FrameworkListenser.cpp::onDataAvailable()
bool FrameworkListener::onDataAvailable(SocketClient *c) {
... ...
len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));
... ...
for (i = 0; i < len; i++) {
if (buffer[i] == '\0') {
/* IMPORTANT: dispatchCommand() expects a zero-terminated string */
dispatchCommand(c, buffer + offset);
offset = i + 1;
}
}
... ...
}
onDataAvailable()函数从socket中读取数据,这些数据是一个或多个以0结尾的字符串,每个字符串都是一条命令。然后调用dispatchCommand()函数来分命令:
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
... ...
for (i = mCommands->begin(); i != mCommands->end(); ++i) {
FrameworkCommand *c = *i;
if (!strcmp(argv[0], c->getCommand())) {
if (c->runCommand(cli, argc, argv)) {
SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
}
goto out;
}
}
... ...
}
dispatchCommand()函数先检查命令的格式是否都正确,然后再检查mCommands中是否有和FrameworkCommand匹配的对象,然后再调用runCommand()函数,在这之前:
CommandListener::CommandListener() :
FrameworkListener("vold", true) {
registerCmd(new DumpCmd());
registerCmd(new VolumeCmd());
registerCmd(new AsecCmd());
registerCmd(new ObbCmd());
registerCmd(new StorageCmd());
registerCmd(new FstrimCmd());
}
CommandListener的构造函数中,通过registerCmd()函数注册了包括 DumpCmd,VolumeCmd等多个cmd命令。
3.2.2 CommandListener::runCommand()
关于CommandListener中的cmd,总共有VolumeCmd,AsecCmd,ObbCmd,StorageCmd,XwarpCmd,cryptfscmd,FstrimCmd, 共7个cmd:
CmdType |
cmd |
explain |
DumpCmd |
|
|
VolumeCmd |
reset |
|
shutdown |
|
|
debug |
|
|
partition(public) |
|
|
partition(private) |
|
|
partition(mixed) |
|
|
mkdirs |
|
|
user_added |
|
|
user_removed |
|
|
user_started |
|
|
user_stopped |
|
|
mount |
|
|
unmount |
|
|
format |
|
|
move_storage |
|
|
benchmark |
|
|
forget_partition |
|
|
StorageCmd |
mountall |
|
|
users |
|
AsecCmd |
|
|
3.3 NativeDaemonConnector
NativeDeamonConnector用于和底层vold进程进行Socket通信,构造函数如下:
NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl,
Looper looper) {
mCallbacks = callbacks;
mSocket = socket;
mResponseQueue = new ResponseQueue(responseQueueSize);
mWakeLock = wl;
if (mWakeLock != null) {
mWakeLock.setReferenceCounted(true);
}
mLooper = looper;
mSequenceNumber = new AtomicInteger(0);
TAG = logTag != null ? logTag : "NativeDaemonConnector";
mLocalLog = new LocalLog(maxLogSize);
}
其中,mCallbacks 就是MountService的引用,mSocket 这里就是传下来的“vold”字符串。
3.3.1 run()
NativeDeamonConnector对象是在MountService的构造函数中被实例化的,然后启动线程时运行run()方法:
public void run() {
mCallbackHandler = new Handler(mLooper, this);
while (true) {
try {
listenToSocket();
} catch (Exception e) {
... ... ...
无限循环,调用listenToSocket()函数:
private void listenToSocket() throws IOException {
... ...
//通过检查“mSocket”来确定vold对应的socket
LocalSocketAddress address = determineSocketAddress();
//连接vold进程中的socket
socket.connect(address);
... ...
//回调MountService中onDaemonConnecte()
mCallbacks.onDaemonConnected();
while (true) {
int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
for (int i = 0; i < count; i++) {
if (buffer[i] == 0) {//每条vold发送的消息都是已“0”结尾的字符串
final String rawEvent = new String(buffer, start, i - start, StandardCharsets.UTF_8);
... ...
try {
//将socket传送的消息放置在event 这个rawEvent中
final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(
rawEvent);
log("RCV <- {" + event + "}");
if (event.isClassUnsolicited()) {
//通过MountService检查处理消息时是否要禁止系统休眠,消息处理完成后解除休眠
if (mCallbacks.onCheckHoldWakeLock(event.getCode())
&& mWakeLock != null) {
mWakeLock.acquire();
releaseWl = true;
}
//收到的是底层的通知消息,发送给MountService
if (mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
event.getCode(), event.getRawEvent()))) {
releaseWl = false;
}
} else {
//如果收到的返回的结果,则放到mResponseQueue消息队列中
mResponseQueue.add(event.getCmdNumber(), event);
}
... .... ...
}
listenToSocket()在无限循环中读取到的socket数据经过parseRawEvent()方法解析成为NativeDaemonEvent对象。从Vold进程中收到的消息分为两种:
- vold上报的通知消息;
- MountService发送的命令的反馈;
以上两种可以通过isClassUnsolicited()方法区分。
Vold上报的消息会通过mCallbackHandler发送,反馈的消息先放入mResponseQueue队列中,在NativeDaemonConnector的execute()方法结束时返回。
- MountService
MountService是systemserver起的binder服务,它的作用就是让用户进程听过它的借口对系统的存储设备进行包括mount,unmount,format等操作,在之前已经分析过了,听过CommandListener向vold进程中下发命令。
4.1 MountService的启动
在SystemServer中启动:
/frameworks/base/services/java/com/android/server/SystemServer.java
private static final String MOUNT_SERVICE_CLASS =
"com.android.server.MountService$Lifecycle";
... ... ...
mSystemServiceManager.startService(MOUNT_SERVICE_CLASS);
mountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
/frameworks/base/services/core/java/com/android/server/SystemServiceManager.java
public <T extends SystemService> T startService(Class<T> serviceClass) {
final String name = serviceClass.getName();
// Create the service.
if (!SystemService.class.isAssignableFrom(serviceClass)) {
... ...
}
final T service;
try {
Constructor<T> constructor = serviceClass.getConstructor(Context.class);
//将service实例化
service = constructor.newInstance(mContext);
} catch (InstantiationException ex) {
... ...
//将service放入自己维护的mService队列上去
mServices.add(service);
try {
//调用service的onStart方法
service.onStart();
} catch (RuntimeException ex) {
... ...
return service;
}
/frameworks/base/services/core/java/com/android/server/MountService.java
public static class Lifecycle extends SystemService {
private MountService mMountService;
public Lifecycle(Context context) {
super(context);
}
@Override
public void onStart() {
mMountService = new MountService(getContext());
publishBinderService("mount", mMountService);
}
在Lifecycle类中的onStart方法中实例化MountService。publishBinderService方法会调用ServiceManager中的addService方法,通过binder
4.1.1MountService构造函数
public MountService(Context context) {
... ...
mCallbacks = new Callbacks(FgThread.get().getLooper());
mPms = (PackageManagerService) ServiceManager.getService("package");
//实例化MountServiceHandler对象,用于处理MountService的流程
mHandler = new MountServiceHandler(hthread.getLooper());
//实例化ObbActionHandler对象,用于处理obb的流程
mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
// Initialize the last-fstrim tracking if necessary
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
// data/system/last-fstrim
mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
... ... ...
// mSettingsFile = data/secure/system/storage.xml
mSettingsFile = new AtomicFile(
new File(Environment.getSystemSecureDirectory(), "storage.xml"));
synchronized (mLock) {
readSettingsLocked();
}
LocalServices.addService(MountServiceInternal.class, mMountServiceInternal);
//初始化mConnecter对象,用于处理MountService和vold进程的通信
mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,null);
... ...
//初始化mCryptConnector与vdc进程的通信
mCryptConnector = new NativeDaemonConnector(this, "cryptd",
MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
... ...
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_ADDED);
userFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
addInternalVolume();
}
private void addInternalVolume() {
// Create a stub volume that represents internal storage
final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
VolumeInfo.TYPE_PRIVATE, null, null);
internal.state = VolumeInfo.STATE_MOUNTED;
internal.path = Environment.getDataDirectory().getAbsolutePath();
mVolumes.put(internal.id, internal);
}
addInternalVolume函数就是添加一个private type的volume对象。
4.1.2 onBootPhase()
由于MountService的内部Lifecycle已添加SystemServiceManager的mServices服务列表,因此系统启动到 PHASE_ACTIVITY_MANAGER_READY 时会回调mServices中的onBootPhase方法:MountService的onBootPhase()主要作用就是最终调用handleSystemReady()函数:
private void handleSystemReady() {
synchronized (mLock) {
resetIfReadyAndConnectedLocked();
}
// Start scheduling nominally-daily fstrim operations
MountServiceIdler.scheduleIdlePass(mContext);
}
其中,比较重要的是resetIfReadyAndConnectedLocked():
private void resetIfReadyAndConnectedLocked() {
... ...
if (mSystemReady && mDaemonConnected) {
killMediaProvider();
mDisks.clear();
mVolumes.clear();
addInternalVolume();
try {
mConnector.execute("volume", "reset");
final UserManager um = mContext.getSystemService(UserManager.class);
final List<UserInfo> users = um.getUsers();
for (UserInfo user : users) {
mConnector.execute("volume", "user_added", user.id, user.serialNumber);
}
for (int userId : mStartedUsers) {
mConnector.execute("volume", "user_started", userId);
}
... ...
作用:
- 调用killMediaProvider()函数:在MountService启动之前,启动MediaProvider时生成的database数据库可能有异常,因此在MountService初始启动(或者更新其维护的存储设备信息)后,强制杀掉MediaProvider进程,强迫其重启,在重启过程中 MediaProvider重新扫描系统的存储设备已更新database信息。
(android原生未考虑SD卡的情况,事实上如果添加的外置SD卡,因为第一次开机时ActivityManagerService处理的流程比较多,会导致com.android.media.provider 的进程实际上是在MountService发送Intent.ACTION_MEDIA_MOUNTED后才被杀掉,MediaProvider启动后就不可能接收到ACTION_MEDIA_MOUNTED)
2.重置MountService维护的:
mDisks:存储设备
mVolumes:卷
- 向vold发送reset,user_add,user_started相关的命令(对disk),这涉及到存储模块的多户用状态的问题,简单点说,vold认为每个它维护的volume对象都应该有一个对应的user,每次初始化volume对象时都需要相应的初始化其对应的user,这样才能做到volume-user之间的对应。
4.1.3 onStartUser()
同样也是SystemServiceManager运行到一定阶段后调用mService中onStartUser()方法:
private void onStartUser(int userId) {
... ...
synchronized (mVolumes) {
for (int i = 0; i < mVolumes.size(); i++) {
final VolumeInfo vol = mVolumes.valueAt(i);
if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) {
final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
mCallbacks.notifyStorageStateChanged(userVol.getPath(), envState, envState);
}
}
mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userId);
}
}
1遍历当前维护的所有volume对象,筛选出当前user可见的,已经正常挂载且可读的对象,通过mHandler 发送Intent.ACTION_MEDIA_MOUNTED的广播,这就是MountService的开机挂在存储设备的广播——未插入SD卡状态下,此广播仅包含内置存储设备的挂在消息;插入SD卡状态下,还包含外置SD卡正常挂载的消息。
2.通过notifyStorageStateChanged回调,通知存储设备的状态已经改变。
5.1开机流程
1.init.lc1860.rc中:
调用 mount_all /fstab.leadcoreinnopower 挂载配置在/device/leadcore/lte26007/fstab.lc1860中各分区的信息,包括system,cache,userdata,kernel,modemarm等。
- init.rc 启动vold进程,vold进程会检查fstab文件中标记有voldmanaged标志的信息,记录在mDiskSources队列中。
- MountService 由SystemServer启动,在构造函数中,增加一个内部存储对象,放在维护的mVolumes队列中
- SystemServer触发AMS回调MountService::onBootPhase(),MountService对vold下发初始化用户状态的命令
- vold中VolumeManager接受到kernel层上报的NetlinkEvent::Action::kAdd uvent消息,获取eventpath,与 流程2 中mDiskSources队列中的disk对象的path进行对比,相同的代表vold进程会进行操作,调用disk->create()。
- vold中disk的create完成后会通知MountService有VOLUME_CREATED。MountService将新增的volume对象添加到维护的mVolumes队列中,并下发mount命令到vold
- vold对volume执行mount操作,上报volume 状态改变,为MEDIA_MOUNTED
- MountService改变mVolumes队列中SD卡对应vol的状态为MEDIA_MOUNTED
9.systemServer触发AMS回调MountService::onStartUser(),MountService对vold下发volume user_started 的命令,并遍历mVolumes队列,发送其中各volume对象的状态。
5.2 用户主动进行T卡操作