本系列主要介绍 Android vold,分为以下篇章
<Android开发> Android vold - 第一篇 vold前言简介
<Android开发> Android vold - 第二篇 vold 的main()函数简介
<Android开发> Android vold - 第三篇 vold 的NetLinkManager类简介
<Android开发> Android vold - 第四篇 vold 的NetlinkHandler类简介
<Android开发> Android vold - 第五篇 vold 的VolumeManager类简介
<Android开发> Android vold - 第六篇 vold 的CommandListener类简介
<Android开发> Android vold - 第七篇 vold 的runCommand()方法解析
继第六篇vold 的CommandListener类涉及的FW数据处理的一些cmd类及类方法;接下具体分析一下上报函数sendMsg()和 接收处理函数runCommand()函数。
10 runCommand()方法解析
第10节的内容主要时对每一个cmd命令解析的过程进行分析。
10.1SocketClient ::sendMsg()方法
在讲解runCommand()之前先来了解一下SocketClient ::sendMsg()方法,这个方法是发送数据给SocketClient 的,vold与FW等SocketClient 通信是依靠这个接口的。
路径:LINUX/android/system/core/libsysutils/src/SocketClient.cpp
int SocketClient::sendMsg(int code, const char *msg, bool addErrno) {
return sendMsg(code, msg, addErrno, mUseCmdNum);
}
//组合data所需要的信息,包括cmd 和 code.
int SocketClient::sendMsg(int code, const char *msg, bool addErrno, bool useCmdNum) {
char *buf;
int ret = 0;
if (addErrno) {
if (useCmdNum) {
ret = asprintf(&buf, "%d %d %s (%s)", code, getCmdNum(), msg, strerror(errno));
} else {
ret = asprintf(&buf, "%d %s (%s)", code, msg, strerror(errno));
}
} else {
if (useCmdNum) {
ret = asprintf(&buf, "%d %d %s", code, getCmdNum(), msg);
} else {
ret = asprintf(&buf, "%d %s", code, msg);
}
}
// Send the zero-terminated message
if (ret != -1) {
ret = sendMsg(buf);
free(buf);
}
return ret;
}
int SocketClient::sendMsg(const char *msg) {
// Send the message including null character
if (sendData(msg, strlen(msg) + 1) != 0) {
SLOGW("Unable to send msg '%s'", msg);
return -1;
}
return 0;
}
//转换传输的数据格式
int SocketClient::sendData(const void *data, int len) {
struct iovec vec[1];
vec[0].iov_base = (void *) data;
vec[0].iov_len = len;
pthread_mutex_lock(&mWriteMutex);
int rc = sendDataLockedv(vec, 1);
pthread_mutex_unlock(&mWriteMutex);
return rc;
}
int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) {
if (mSocket < 0) {
errno = EHOSTUNREACH;
return -1;
}
if (iovcnt <= 0) {
return 0;
}
int ret = 0;
int e = 0; // SLOGW和sigaction对于errno不是惰性的
int current = 0;
struct sigaction new_action, old_action;
memset(&new_action, 0, sizeof(new_action));
new_action.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &new_action, &old_action);
for (;;) {
ssize_t rc = TEMP_FAILURE_RETRY(
writev(mSocket, iov + current, iovcnt - current));
if (rc > 0) {
size_t written = rc;
while ((current < iovcnt) && (written >= iov[current].iov_len)) {
written -= iov[current].iov_len;
current++;
}
if (current == iovcnt) {
break;
}
iov[current].iov_base = (char *)iov[current].iov_base + written;
iov[current].iov_len -= written;
continue;
}
if (rc == 0) {
e = EIO;
SLOGW("0 length write :(");
} else {
e = errno;
SLOGW("write error (%s)", strerror(e));
}
ret = -1;
break;
}
sigaction(SIGPIPE, &old_action, &new_action);
if (e != 0) {
errno = e;
}
return ret;
}
由上述函数调用可分析出SocketClient ::sendMsg()方法最后是调用 writev(mSocket, iov + current, iovcnt - current));进行写数据发送的。
10.2 DumpCmd::runCommand()方法
路径:LINUX/android/system/vold/CommandListener.cpp
int CommandListener::DumpCmd::runCommand(SocketClient *cli,
int /*argc*/, char ** /*argv*/) {
cli->sendMsg(0, "Dumping loop status", false);
if (Loop::dumpState(cli)) {
cli->sendMsg(ResponseCode::CommandOkay, "Loop dump failed", true);
}
cli->sendMsg(0, "Dumping DM status", false);
if (Devmapper::dumpState(cli)) {
cli->sendMsg(ResponseCode::CommandOkay, "Devmapper dump failed", true);
}
cli->sendMsg(0, "Dumping mounted filesystems", false);
FILE *fp = fopen("/proc/mounts", "re");
if (fp) {
char line[1024];
while (fgets(line, sizeof(line), fp)) {
line[strlen(line)-1] = '\0';
cli->sendMsg(0, line, false);;
}
fclose(fp);
}
cli->sendMsg(ResponseCode::CommandOkay, "dump complete", false);
return 0;
}
第4行:响应发送消息“Dumping loop status”给 SocketClient ;sendMsg()函数是SocketClient类的方法;
第5行:调用Loop::dumpState(cli),获取state信息,
路径:LINUX/android/system/vold/Loop.cpp
int Loop::dumpState(SocketClient *c) {
int i;
int fd;
char filename[256];
for (i = 0; i < LOOP_MAX; i++) {
struct loop_info64 li;
int rc;
snprintf(filename, sizeof(filename), "/dev/block/loop%d", i);
if ((fd = open(filename, O_RDWR | O_CLOEXEC)) < 0) {
if (errno != ENOENT) {
SLOGE("Unable to open %s (%s)", filename, strerror(errno));
} else {
continue;
}
return -1;
}
rc = ioctl(fd, LOOP_GET_STATUS64, &li);
close(fd);
if (rc < 0 && errno == ENXIO) {
continue;
}
if (rc < 0) {
SLOGE("Unable to get loop status for %s (%s)", filename,
strerror(errno));
return -1;
}
char *tmp = NULL;
asprintf(&tmp, "%s %d %lld:%lld %llu %lld:%lld %lld 0x%x {%s} {%s}", filename, li.lo_number,
MAJOR(li.lo_device), MINOR(li.lo_device), li.lo_inode, MAJOR(li.lo_rdevice),
MINOR(li.lo_rdevice), li.lo_offset, li.lo_flags, li.lo_crypt_name,
li.lo_file_name);
c->sendMsg(0, tmp, false);
free(tmp);
}
return 0;
}
从Loop::dumpState()的内容可看出,主要是读取 “/dev/block/loop”的内容,然后发送给SocketClient。
具体信息是存储在结构体loop_info64中:
struct loop_info64 {
__u64 lo_device; /* ioctl r/o */
__u64 lo_inode; /* ioctl r/o */
__u64 lo_rdevice; /* ioctl r/o */
__u64 lo_offset;
__u64 lo_sizelimit;/* bytes, 0 == max available */
__u32 lo_number; /* ioctl r/o */
__u32 lo_encrypt_type;
__u32 lo_encrypt_key_size; /* ioctl w/o */
__u32 lo_flags; /* ioctl r/o */
__u8 lo_file_name[LO_NAME_SIZE];
__u8 lo_crypt_name[LO_NAME_SIZE];
__u8 lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
__u64 lo_init[2];
};
详细介绍可参考:https://www.onitroad.com/jc/linux/man-pages/linux/man4/loop.4.html
第6行:发送消息“Loop dump failed”给 SocketClient;
第8行:发送消息“Dumping DM status”给 SocketClient;
第9行:主要是调用 Devmapper::dumpState(SocketClient *c),函数输出DM的状态;主要还是读取“/dev/device-mapper”的内容放到 struct dm_ioctl 结构体中;该结构体如下:
/*所有ioctl参数都由一块内存组成,在开始时具有此结构。如果指定了uuid,则将对其进行任何查找(例如DM_INFO),而不是*名称*/
struct dm_ioctl{
/*版本号由三部分组成:主要-不向后或向前兼容,次要-仅向后兼容,补丁-向后和向前兼容。ioctl接口的所有客户端都应该填写编译时使用的接口的版本号。
所有识别的ioctl命令(即不返回-ENOTTY的命令)都填写此字段,即使命令失败。
*/
__u32version[3];/*输入/输出*/
__u32data_size;/*传入的数据(包括此结构)的总大小*/
__u32data_start;/*相对于此结构开头的数据开头偏移量*/
__u32target_count;/*输入/输出*/
__s32open_count;/*输出*/
__u32 flags;/*输入/输出*/
/*eventnr保存事件编号(输入和输出)或udev cookie值(仅输入)。DM_DEV_WAIT ioctl将事件编号作为输入。DM_SUSPEND、DM_DEV_REMOVE和DM_DEV_RENAME ioctl将该字段用作cookie,以在DM_cookie变量中返回它们发出的事件。对于输出,ioctl返回事件编号,而不是cookie*/
__u32 event_nr;/*输入/输出*/
__u32 padding;
__u64 dev;/*输入/输出*/
char name[DM_name_LEN];/*设备名称*/
char uuid[DM_UID_LEN];/*块设备的唯一标识符*/
char data[7];/*填充或数据*/
};
详细可参考其它网友介绍:https://www.cnblogs.com/pyjetson/p/14872499.html
第10行:发送消息“Devmapper dump failed”给 SocketClient;
第12行:发送消息“Dumping mounted filesystems”给 SocketClient;
第13行:打开文件“/proc/mounts”;
第14行:判断打开文件“/proc/mounts”成功;
第16行:循环读取“/proc/mounts”的内容;
第17行:在读取数据的结尾加上结束符;
第18行:将读取的数据发送给SocketClient;
第20行:关闭文件;
第23行:发送消息“dump complete”给 SocketClient;
总结,DumpCmd::runCommand()方法主要是获取一些设备的的信息。
10.3 VolumeCmd::runCommand()方法
路径:LINUX/android/system/vold/CommandListener.cpp
int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
dumpArgs(argc, argv, -1);
if (argc < 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
return 0;
}
VolumeManager *vm = VolumeManager::Instance();
std::lock_guard<std::mutex> lock(vm->getLock());
// TODO: tease out methods not directly related to volumes
//TODO:梳理与卷不直接相关的方法
std::string cmd(argv[1]);
if (cmd == "reset") {
return sendGenericOkFail(cli, vm->reset());
} else if (cmd == "shutdown") {
return sendGenericOkFail(cli, vm->shutdown());
} else if (cmd == "debug") {
return sendGenericOkFail(cli, vm->setDebug(true));
} else if (cmd == "partition" && argc > 3) {
// partition [diskId] [public|private|mixed] [ratio]
std::string id(argv[2]);
auto disk = vm->findDisk(id);
if (disk == nullptr) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown disk", false);
}
std::string type(argv[3]);
if (type == "public") {
return sendGenericOkFail(cli, disk->partitionPublic());
} else if (type == "private") {
return sendGenericOkFail(cli, disk->partitionPrivate());
} else if (type == "mixed") {
if (argc < 4) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
}
int frac = atoi(argv[4]);
return sendGenericOkFail(cli, disk->partitionMixed(frac));
} else {
return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
}
} else if (cmd == "mkdirs" && argc > 2) {
// mkdirs [path]
return sendGenericOkFail(cli, vm->mkdirs(argv[2]));
} else if (cmd == "user_added" && argc > 3) {
// user_added [user] [serial]
return sendGenericOkFail(cli, vm->onUserAdded(atoi(argv[2]), atoi(argv[3])));
} else if (cmd == "user_removed" && argc > 2) {
// user_removed [user]
return sendGenericOkFail(cli, vm->onUserRemoved(atoi(argv[2])));
} else if (cmd == "user_started" && argc > 2) {
// user_started [user]
return sendGenericOkFail(cli, vm->onUserStarted(atoi(argv[2])));
} else if (cmd == "user_stopped" && argc > 2) {
// user_stopped [user]
return sendGenericOkFail(cli, vm->onUserStopped(atoi(argv[2])));
} else if (cmd == "mount" && argc > 2) {
// mount [volId] [flags] [user]
std::string id(argv[2]);
auto vol = vm->findVolume(id);
if (vol == nullptr) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
}
int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;
vol->setMountFlags(mountFlags);
vol->setMountUserId(mountUserId);
int res = vol->mount();
if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {
vm->setPrimary(vol);
}
return sendGenericOkFail(cli, res);
} else if (cmd == "unmount" && argc > 2) {
// unmount [volId]
std::string id(argv[2]);
auto vol = vm->findVolume(id);
if (vol == nullptr) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
}
return sendGenericOkFail(cli, vol->unmount());
} else if (cmd == "format" && argc > 3) {
// format [volId] [fsType|auto]
std::string id(argv[2]);
std::string fsType(argv[3]);
auto vol = vm->findVolume(id);
if (vol == nullptr) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
}
return sendGenericOkFail(cli, vol->format(fsType));
} else if (cmd == "move_storage" && argc > 3) {
// move_storage [fromVolId] [toVolId]
auto fromVol = vm->findVolume(std::string(argv[2]));
auto toVol = vm->findVolume(std::string(argv[3]));
if (fromVol == nullptr || toVol == nullptr) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
}
(new android::vold::MoveTask(fromVol, toVol))->start();
return sendGenericOkFail(cli, 0);
} else if (cmd == "benchmark" && argc > 2) {
// benchmark [volId]
std::string id(argv[2]);
nsecs_t res = vm->benchmarkPrivate(id);
return cli->sendMsg(ResponseCode::CommandOkay,
android::base::StringPrintf("%" PRId64, res).c_str(), false);
} else if (cmd == "forget_partition" && argc > 2) {
// forget_partition [partGuid]
std::string partGuid(argv[2]);
return sendGenericOkFail(cli, vm->forgetPartition(partGuid));
} else if (cmd == "remount_uid" && argc > 3) {
// remount_uid [uid] [none|default|read|write]
uid_t uid = atoi(argv[2]);
std::string mode(argv[3]);
return sendGenericOkFail(cli, vm->remountUid(uid, mode));
}
return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
}
VolumeCmd::runCommand()方法主要是对Volume的一些操作,包括挂载、卸载、复位、关机、分区、创建目录、新增用户、删除用户等等cmd操作;具体操作以FW传输的cmd为准。由于操作cmd太对,就不一一解释。这里主要讲一下mount的内容;
else if (cmd == "mount" && argc > 2) {
// mount [volId] [flags] [user]
std::string id(argv[2]);
auto vol = vm->findVolume(id);
if (vol == nullptr) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
}
int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;
vol->setMountFlags(mountFlags);
vol->setMountUserId(mountUserId);
int res = vol->mount();
if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {
vm->setPrimary(vol);
}
return sendGenericOkFail(cli, res);
}
mount的内容入上截取部分;
首先通过findVolume()查找操作的Volume对象是否存在;
第二,setMountFlags()设置挂载的标志 和 setMountUserId()设置挂载的用户ID;
第三,调用mount()函数进行挂载操作;
mount()函数在“LINUX/android/system/vold/VolumeBase.cpp”中实现,如下:
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;
}
具体是调用doMount()函数实现;
doMount()函数的内容比较长,就不具体分析,想了解的可自行查阅函数源码“LINUX/android/system/vold/PublicVolume.cpp”;主要是根据不同的文件系统类型,调用 系统调用mount进行挂载,并赋予权限。
10.4StorageCmd::runCommand()方法
LINUX/android/system/vold/CommandListener.cpp
10.5AsecCmd::runCommand()方法
LINUX/android/system/vold/CommandListener.cpp
10.6ObbCmd::runCommand()方法
LINUX/android/system/vold/CommandListener.cpp
10.7FstrimCmd::runCommand()方法
LINUX/android/system/vold/CommandListener.cpp
10.8AppFuseCmd::runCommand()方法
LINUX/android/system/vold/CommandListener.cpp
上述的方法,都是根据具体解析 arg cmd调用对应的函数进行处理。读者可以根据具体cmd具体分析。
综上对vold的流程分析已完成;后续读者可根据后续遇到的问题再深入分析;
…欢迎联系作者沟通交流,qq759521350…