編輯:關於Android編程
一、Vold工作機制
Vold是Volume Daemon的縮寫,它是Android平台中外部存儲系統的管控中心,是管理和控制Android平台外部存儲設備的後台進程。其功能主要包括:SD卡的插拔事件檢測、SD卡掛載、卸載、格式化等。

如上圖所示,Vold中的NetlinkManager模塊接收來自Linux Kernel的uevent消息。
NetlinkManager將這些消息轉發給VolumeManager模塊。VolumeManager會對應做一些操作,然後把相關信息通過CommandListener發送給MountService。
MountService根據收到的消息後,根據情況會利用CommandListener發送相關的處理命令給VolumeManager做進一步處理。
CommandListener模塊內部封裝了一個Socket用於跨進程通信。它一方面接收來自MountService的控制命令,另一方面VolumeManager通過它將消息發送給MountService。
Tips:
Netlink是Linux系統中用戶空間進程和Kernel進行通信的一種機制,是基於Socket的異步通信機制。
通過這種機制,位於用戶空間的進程可以接收來自Kernel的一些信息,同時用戶空間進程也可以利用Netlink向Kernel發送一些控制命令。
二、Vold進程啟動過程
Vold進程啟動文件定義於system/vold/vold.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
ioprio be 2
writepid /dev/cpuset/foreground/tasks
被init進程啟動後,將調用system/vold/main.cpp中的main函數:
int main(int argc, char** argv) {
.............
VolumeManager *vm;
CommandListener *cl;
.............
NetlinkManager *nm;
//解析vold.rc中定義的blkid和fsck相關的參數
parse_args(argc, argv);
..............
// Quickly throw a CLOEXEC on the socket we just inherited from init
//這裡的含義不是很明白
//對於fcntl(fd, F_SETFD, FD_CLOEXEC)函數
//FD_CLOEXEC表示當程序執行exec函數時, fd將被系統自動關閉, 不傳遞給exec創建的新進程,以免fd在子進程中仍然有效
//但在init進程中,是先fork出子進程,然後在進程中創建出socket,才執行exec函數,有必要使用fcntl函數麼?
fcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC);
fcntl(android_get_control_socket("cryptd"), F_SETFD, FD_CLOEXEC);
//創建文件夾/dev/block/vold
mkdir("/dev/block/vold", 0755);
.........
//創建VolumeManager
if (!(vm = VolumeManager::Instance())) {
LOG(ERROR) << "Unable to create VolumeManager";
exit(1);
}
//創建NetlinkManager
if (!(nm = NetlinkManager::Instance())) {
LOG(ERROR) << "Unable to create NetlinkManager";
exit(1);
}
...................
//創建CommandListener
cl = new CommandListener();
............
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
//啟動VolumeManager
if (vm->start()) {
PLOG(ERROR) << "Unable to start VolumeManager";
exit(1);
}
//根據配置文件初始化VolumeManager
if (process_config(vm)) {
PLOG(ERROR) << "Error reading configuration... continuing anyways";
}
//啟動NetlinkManager
if (nm->start()) {
PLOG(ERROR) << "Unable to start NetlinkManager";
exit(1);
}
//與ueventd進程進行冷啟動類似,此處通過往/sys/block目錄下對應的uevent文件寫"add\n"來觸發內核發送uevent消息
coldboot("/sys/block");
//啟動CommandListener
if (cl->startListener()) {
PLOG(ERROR) << "Unable to start CommandListener";
exit(1);
}
.......
// Eventually we'll become the monitoring thread
while(1) {
sleep(1000);
}
LOG(ERROR) << "Vold exiting";
exit(0);
}
從上面的代碼不難看出,Vold進程的main函數中,創建並啟動其子模塊VolumeManager、NetlinkManager和CommandListener後,就不再執行實際的工作了。
以後Vold進程具體的工作就會交付給子模塊進行處理。
三、Vold進程中各模塊分析
為了進一步了解整個Vold進程的主要工作流程,接下來我們分析一下其主要模塊的工作流程。
1、NetlinkManager模塊
1.1 NetlinkManager的創建和啟動
在Vold的main函數中,調用NetlinkManager::Instance創建出NetlinkManager:
NetlinkManager *NetlinkManager::Instance() {
if (!sInstance)
sInstance = new NetlinkManager();
return sInstance;
}
//mBroadcaster的類型為SocketListener
NetlinkManager::NetlinkManager() {
mBroadcaster = NULL;
}
從上面的代碼可以看到,NetlinkManager的創建比較簡單。
在創建出NetlinkManager後,Vold調用了NetlinkManager的setBroadcaster函數:
void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
依然言簡意赅。
這裡唯一需要說明的是,Android這裡的設計看起來比較很奇怪,雖然NetlinkManager設置了CommandListener對象,但它並沒有通過CommandListener發送消息和接收命令。
配置好NetlinkManager後,Vold就調用了NetlinkManger的start函數:
int NetlinkManager::start() {
//以下定義並初始化socket的地址結構
struct sockaddr_nl nladdr;
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;
//創建PF_NETLINK地址簇的socket,NETLINK_KOBJECT_UEVENT表示該socket將接收內核的Uevent事件
if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,
NETLINK_KOBJECT_UEVENT)) < 0) {
SLOGE("Unable to create uevent socket: %s", strerror(errno));
return -1;
}
//setsockopt設置socket的選項,此處設置socket的接收緩沖區大小為64 * 1024
if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
SLOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));
goto out;
}
//此處設置允許接收憑證相關的信息
if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
goto out;
}
//將創建出的socket綁定到之前的地址上,此時socket可以收到Kernel的數據了
if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
SLOGE("Unable to bind uevent socket: %s", strerror(errno));
goto out;
}
//創建並啟動一個NetlinkHandler
mHandler = new NetlinkHandler(mSock);
if (mHandler->start()) {
SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
goto out;
}
return 0;
out:
close(mSock);
return -1;
}
通過上面的代碼不難看出,其實NetlinkManager啟動後就是創建一個可以接收Kernel消息的socket,並以此socket構建並啟動NetlinkHandler。
可以預見NetlinkHandler將用來處理socket收到的信息。
1.2 NetlinkHandler
NetlinkHandler::NetlinkHandler(int listenerSocket) :
NetlinkListener(listenerSocket) {
}
NetlinkHandler初始化時,將與Kernel通信的socket描述符傳入到父類NetlinkListener中。
NetlinkListener::NetlinkListener(int socket) :
SocketListener(socket, false) {
mFormat = NETLINK_FORMAT_ASCII;
}
NetlinkListener又進一步調用其父類SocketListener:
SocketListener::SocketListener(int socketFd, bool listen) {
init(NULL, socketFd, listen, false);
}
//socektName為null, listen和useCmdNum的值均為false
void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
mListen = listen;
mSocketName = socketName;
mSock = socketFd;
mUseCmdNum = useCmdNum;
//初始化一個mutex
pthread_mutex_init(&mClientsLock, NULL);
//SocketClientCollection用於存儲與Socket服務端通信的客戶端
mClients = new SocketClientCollection();
}
從上面的代碼可以看出,NetlinkHandler對應的繼承體系如下圖所示:

創建完NetlinkHandler後,NetlinkManager調用了NetlinkHandler的start方法:
int NetlinkHandler::start() {
//根據繼承體系,實際上調用了SocketListener的startListenr函數
return this->startListener();
}
int SocketListener::startListener() {
return startListener(4);
}
int SocketListener::startListener(int backlog) {
//前面代碼已經提及,構造NetlinkHandler時,mSocketName為null,略去部分代碼
................
//mListen的參數也為false,表明mSocket並不是一個服務器端
if (mListen && listen(mSock, backlog) < 0) {
..............
} else if (!mListen)
//利用mSocket構造SocketClient加入到mClients中
//這個SocketClient並不是真實客戶端的代表,此處只是為了代碼和操作的統一
mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
//pipe系統調用將創建一個匿名管道,mCtrlPipe是一個int類型的二元數組
//其中mCtrlPipe[0]用於從管道讀數據,mCtrlPipe[1]用於往管道寫數據
if (pipe(mCtrlPipe)) {
SLOGE("pipe failed (%s)", strerror(errno));
return -1;
}
//創建一個工作線程,線程的執行函數為threadStart
if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
SLOGE("pthread_create (%s)", strerror(errno));
return -1;
}
return 0;
}
至此,我們知道了NetlinkHandler啟動後,創建了一個工作線程,用於接收和處理數據。現在進一步看看threadStart函數:
void *SocketListener::threadStart(void *obj) {
SocketListener *me = reinterpret_cast(obj);
//調用SocketListener的runListener函數
me->runListener();
pthread_exit(NULL);
return NULL;
}
void SocketListener::runListener() {
SocketClientCollection pendingList;
//無線循環,接收socket收到的數據
while(1) {
SocketClientCollection::iterator it;
fd_set read_fds;
int rc = 0;
int max = -1;
//將指定的文件描述符集清空,系統分配時默認是不清空的
FD_ZERO(&read_fds);
..........
//在文件描述符集中增加一個新的描述符
FD_SET(mCtrlPipe[0], &read_fds);
//max將與select函數
if (mCtrlPipe[0] > max)
max = mCtrlPipe[0];
pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {
// NB: calling out to an other object with mClientsLock held (safe)
int fd = (*it)->getSocket();
FD_SET(fd, &read_fds);
if (fd > max) {
max = fd;
}
}
pthread_mutex_unlock(&mClientsLock);
...............
//監聽是否有數據到來
if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
............
} else if (!rc)
continue;
//FD_ISSET用於測試指定的文件描述符是否在該集合中,前面已經加入了
if (FD_ISSET(mCtrlPipe[0], &read_fds)) {
char c = CtrlPipe_Shutdown;
TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));
//如果從管道中讀出CtrlPipe_Shutdown,則退出工作線程
if (c == CtrlPipe_Shutdown) {
break;
}
continue;
}
//如果mSock是服務器端,進入這個分支,NetlinkHandler中的mSock並不是服務器端,此處僅作了解
if (mListen && FD_ISSET(mSock, &read_fds)) {
sockaddr_storage ss;
sockaddr* addrp = reinterpret_cast(&ss);
socklen_t alen;
int c;
do {
alen = sizeof(ss);
//服務器端接收客戶端請求
c = accept(mSock, addrp, &alen);
SLOGV("%s got %d from accept", mSocketName, c);
} while (c < 0 && errno == EINTR);
if (c < 0) {
SLOGE("accept failed (%s)", strerror(errno));
sleep(1);
continue;
}
fcntl(c, F_SETFD, FD_CLOEXEC);
pthread_mutex_lock(&mClientsLock);
//將新的客戶端請求加入到mClients中
mClients->push_back(new SocketClient(c, true, mUseCmdNum));
pthread_mutex_unlock(&mClientsLock);
}
/* Add all active clients to the pending list first */
//這裡引入了pendingList,主要是針對服務端提出的
//當mSocket是服務端的時候,上面的代碼將會增加新的mClient,但在下一次循環之前,這些mClient還未被加入到read_fds中
pendingList.clear();
pthread_mutex_lock(&mClientsLock);
//由於NetlinkHandler中的mSocket不是服務端,因此mClients中實際上只有mSocket自己(前面startListener中加入的)
for (it = mClients->begin(); it != mClients->end(); ++it) {
SocketClient* c = *it;
// NB: calling out to an other object with mClientsLock held (safe)
int fd = c->getSocket();
if (FD_ISSET(fd, &read_fds)) {
//待處理的SocketClient加入到pendingList中
pendingList.push_back(c);
c->incRef();
}
}
pthread_mutex_unlock(&mClientsLock);
while (!pendingList.empty()) {
it = pendingList.begin();
SocketClient* c = *it;
pendingList.erase(it);
//調用子類的onDataAvailable函數處理收到的數據
if (!onDataAvailable(c)) {
//返回false時,需要關閉該SocketClient
//關閉SocketClient將直接操作mClients對象
release(c, false);
}
c->decRef();
}
}
}
上面的代碼看起來比較復雜,主要是因為考慮到了Socket作為服務端的情況。
在NetlinkHandler中Socket僅作為客戶端接收數據,因此在上面的代碼中,其實就是利用子類的onDataAvailable函數處理收到的數據而已。
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
int socket = cli->getSocket();
ssize_t count;
uid_t uid = -1;
.................
//這裡用uevent_kernel_recv函數,從socket中取出Uevent數據
count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,
mBuffer, sizeof(mBuffer), require_group, &uid));
if (count < 0) {
........
return false;
}
NetlinkEvent *evt = new NetlinkEvent();
//mFormat初始化時指定為NETLINK_FORMAT_ASCII
//此處將Uevent數據解碼成NetlinkEvent,然後調用子類的onEvent進行處理
if (evt->decode(mBuffer, count, mFormat)) {
onEvent(evt);
} else if (mFormat != NETLINK_FORMAT_BINARY) {
..........
}
delete evt;
return true;
}
上面的代碼比較簡單,其實就是從socket中的字節流中取出Uevent事件,然後將這些事件解碼成NetlinkEvent,然後利用子類的onEvent做進一步處理。
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")) {
//將NetlinkEvent遞交給VolumeManager處理
vm->handleBlockEvent(evt);
}
}

最後總結一下NetlinkManager模塊的工作,如上圖所示:
1、NetlinkManager啟動後,將創建出與Kernel通信的socket,並用此socket創建出NetlinkHandler。
2、NetlinkHandler啟動後,將創建出工作線程(其父類函數完成)。
3、工作線程啟動後,將負責監聽socket是否有數據到來。
4、當工作線程監聽到數據到來後,負責將數據遞交給NetlinkHandler。
5、NetlinkHandler負責從socket中的數據中解析出Uevent,並進一步解碼成NetlinkEvent,以遞交給VolumeManager。
2、VolumeManager模塊
2.1 VolumeManager的創建和啟動
在Vold的main函數中,調用VolumeManager的instance函數創建VolumeManager:
VolumeManager *VolumeManager::Instance() {
if (!sInstance)
sInstance = new VolumeManager();
return sInstance;
}
VolumeManager::VolumeManager() {
mDebug = false;
mActiveContainers = new AsecIdCollection();
mBroadcaster = NULL;
mUmsSharingCount = 0;
mSavedDirtyRatio = -1;
// set dirty ratio to 0 when UMS is active
mUmsDirtyRatio = 0;
}
容易看出VolumeManager也是單例模式創建的。
接著,Vold進程利用VolumeManager的setBroadcaster函數,將Commandlistener對象賦予VolumeManager。
void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
完成VolumeManager的創建後,Vold進程調用start函數,啟動VolumeManager:
int VolumeManager::start() {
// Always start from a clean state by unmounting everything in
// directories that we own, in case we crashed.
unmountAll();
// Assume that we always have an emulated volume on internal
// storage; the framework will decide if it should be mounted.
CHECK(mInternalEmulated == nullptr);
mInternalEmulated = std::shared_ptr(
new android::vold::EmulatedVolume("/data/media"));
mInternalEmulated->create();
return 0;
}
從上面的代碼可以看出,VolumeManager啟動後就干了兩件事:
1、清楚所有已掛載的設備。正如注釋所說的,通過這種方式可以讓VolumeManager每次都從一個確定的“干淨”的狀態啟動,避免之前出現Vold進程出現過crash。
看看unmountAll函數:
int VolumeManager::unmountAll() {
std::lock_guard lock(mLock);
// First, try gracefully unmounting all known devices
if (mInternalEmulated != nullptr) {
mInternalEmulated->unmount();
}
for (auto disk : mDisks) {
disk->unmountAll();
}
// Worst case we might have some stale mounts lurking around, so
// force unmount those just to be safe.
FILE* fp = setmntent("/proc/mounts", "r");
if (fp == NULL) {
SLOGE("Error opening /proc/mounts: %s", strerror(errno));
return -errno;
}
// Some volumes can be stacked on each other, so force unmount in
// reverse order to give us the best chance of success.
std::list toUnmount;
mntent* mentry;
while ((mentry = getmntent(fp)) != NULL) {
if (strncmp(mentry->mnt_dir, "/mnt/", 5) == 0
|| strncmp(mentry->mnt_dir, "/storage/", 9) == 0) {
toUnmount.push_front(std::string(mentry->mnt_dir));
}
}
endmntent(fp);
for (auto path : toUnmount) {
SLOGW("Tearing down stale mount %s", path.c_str());
android::vold::ForceUnmount(path);
}
return 0;
}
unmountAll的內容比較簡單,同時注釋清晰,此處不再贅述。
2、創建一個內部的掛載設備。
mInternalEmulated是一個VolumeBase類型的對象,我們看看其create函數:
status_t VolumeBase::create() {
CHECK(!mCreated);
mCreated = true;
//doCreate進行實際的創建
status_t res = doCreate();
//通過CommandListener通知框架中的MountService
notifyEvent(ResponseCode::VolumeCreated,
StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str()));
setState(State::kUnmounted);
return res;
}
2.2 配置VolumeManager
當Vold創建並啟動完VolumeManager後,就調用process_config函數對VolumeManager進行配置:
static int process_config(VolumeManager *vm) {
//讀取默認的fstab文件
std::string path(android::vold::DefaultFstabPath());
fstab = fs_mgr_read_fstab(path.c_str());
...........
/* Loop through entries looking for ones that vold manages */
........
for (int i = 0; i < fstab->num_entries; i++) {
//是否能被被vold管理
if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
//根據fstab文件中設備的信息構造sysPattern、nickname和flags
...........
//
vm->addDiskSource(std::shared_ptr(
new VolumeManager::DiskSource(sysPattern, nickname, flags)));
}
}
................
}
結合代碼,我們知道process_config其實就是解析fstab文件,然後設置一些存儲設備的掛載點。
2.3 NetlinkManager與VolumeManager之間的交互
在前面介紹NetlinkManager時,我們知道當NetlinkManager收到Kernel的事件後,將利用NetlinkHandler通知VolumeManager:
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的handleBlockeEvent:
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
..............
//取出主設備號和次設備號
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: {
//創建新的disk
.........
auto disk = new android::vold::Disk(eventPath, device,
source->getNickname(), flags);
disk->create();
mDisks.push_back(std::shared_ptr(disk));
break;
}
case NetlinkEvent::Action::kChange: {
..........
//改變對應的disk信息
for (auto disk : mDisks) {
if (disk->getDevice() == device) {
disk->readMetadata();
disk->readPartitions();
}
}
break;
}
case NetlinkEvent::Action::kRemove: {
//移除對應的disk
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;
}
}
}
至此VolumeManager的主要工作介紹完畢,從上面的代碼可以看出VolumeManager使用Disk對象來抽象實際的存儲設備。

我們現在可以結合上圖總結一下VolumeManager的工作流程:
1、存儲設備發生變化(如熱插拔等),將導致Linux Kernel發出Uevent消息給NetlinkManager。
2、NetlinkManager將事件通知給VolumeManager。
3、VolumeManager根據事件的內容,判斷是設備的變化情況,然後操作對應的Disk對象。例如新增存儲設備,就利用事件中的內容創建出新的Disk(創建Disk時,就會進一步讀取分區信息,創建出Volume對象,此處不再細分);設備被移除了,VolumeManager就負責移除對應的Disk。
到目前為止,我們分析的流程都停留在Vold進程中,並沒有與Android框架發生實際的交互。為了引出交互的實際流程,我們需要先分析一下Vold進程與框架交互的橋梁,即CommandListener。
3、CommandListener
3.1 CommandListener的創建
Vold進程在main函數中創建出了CommandListener,然後調用了CommandListener的startListener函數。
我們先看看CommandListener的構造函數:
class CommandListener : public FrameworkListener {
........
}
從定義來看CommandListener繼承於FrameworkListener。
CommandListener::CommandListener() :
FrameworkListener("vold", true) {
//注冊CommandListener支持的命令
registerCmd(new DumpCmd());
registerCmd(new VolumeCmd());
registerCmd(new AsecCmd());
registerCmd(new ObbCmd());
registerCmd(new StorageCmd());
registerCmd(new FstrimCmd());
registerCmd(new AppFuseCmd());
}
在CommandListener的構造函數中,調用了父類的構造函數,同時利用其父類的registerCmd函數創建並注冊了一些Cmd對象。
我們看看FrameworkListener:
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
SocketListener(socketName, true, withSeq) {
init(socketName, withSeq);
}
void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) {
mCommands = new FrameworkCommandCollection();
errorRate = 0;
mCommandCount = 0;
mWithSeq = withSeq;
}
void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
//將注冊的Command保存到列表中
mCommands->push_back(cmd);
}

根據前面的代碼,我們可以得到上圖的繼承關系。
可以看到與之前分析NetlinkManager一樣,CommandListener最終繼承自SocketListener。不過與NetlinkManager不同的是,CommandListener傳入到SocketListener的mListen參數為true,這意味著CommandListener中的socket將作為服務端存在。
從FrameworkListener的registerCmd函數來看,FrameworkListener僅僅是保存了新創建的Cmd對象。這裡采用了設計模式中的Command模式,每個命令的處理函數都是runCommand。
3.2 CommandListener啟動
當Vold進程創建出CommandListener後,同樣調用了CommandListener的startListener函數。
根據繼承關系,我們知道最終將會調用到SocketListener的startListener函數。
在分析NetlinkManager時,我們已經分析過SocketListener的startListener函數。在startListener函數中將啟動一個工作線程,以監聽對應socket的數據。
此處CommandListener監聽的是init進程創建出Vold進程後,Vold進程創建的名為”vold”的socket,並且該socket是作為服務端存在的。當服務端收到注冊請求後,將生成對應的SocketClient對象。然後,工作線程就可以監聽SocketClient是否有數據到來。
“vold”的客戶端是MountService。與之前分析的一樣,當工作線程收到客戶端數據時,將調用子類的onDataAvailable函數進行處理。
此時SocketListener的子類是FrameworkListener:
bool FrameworkListener::onDataAvailable(SocketClient *c) {
char buffer[CMD_BUF_SIZE];
int len;
//將socket的數據讀入到buffer中
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;
}
}
return true;
}
//解析並分發Command
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
//從data中解析出Command的參數
............
for (i = mCommands->begin(); i != mCommands->end(); ++i) {
FrameworkCommand *c = *i;
//根據參數判斷是否能被CommandListener中注冊的命令處理
if (!strcmp(argv[0], c->getCommand())) {
//調用對應Command的runCommand函數
if (c->runCommand(cli, argc, argv)) {
SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
}
goto out;
}
}
............
}
以DumpCommand舉例,看看runCommand函數:
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", "r");
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;
}
從上面的代碼容易看出,DumpCmd執行相應的操作後,都是通過SocketClient的sendMsg發送結果。在SocketClient的底層,就是靠”vold” socket將數據返回給MountService。
現在我們總結一下CommandListener涉及的工作流程:

如上圖所示:
1、init進程啟動Vold進程時,根據vold.rc創建了”vold” socket,”vold” socket作為server端存在於Vold進程中。
2、在Vold進程的main函數中,創建出了CommandListener(部分工作尤其父類完成);CommandListener創建一些Cmd。
3、調用CommandListener的startListener函數,尤其父類SocketListener中創建出實際的工作線程,監聽”vold” socket是否有請求到來。
4、框架中的MountService啟動後,間接利用socket與”vold”通信(通過NativeDaemonConnector封裝)。初始時,將向”vold”發送connect請求。
5、當工作線程監聽到”vold”有請求到來後,利用accept函數創建出與MountService端通信的server端,即上圖的s。接下來,工作線程開始監聽s上是否有數據到來。
6、當工作線程監聽到s有數據到來後,將數據遞交給CommandListener(實際是FrameworkListener處理)。
7、CommandListener根據數據的類型,調用對應的Command進行處理。
8、實際的Cmd根據參數進行實際的操作,然後將運行結果遞交給s,s再將數據通過c遞交給MountService。
接下來,我們看看運行在框架層中的MountService。
4 MountService
有些應用程序需要檢測外部存儲卡的插入/拔出事件,這些事件由MountService通過Intent廣播發送。例如外部存儲卡插入後,MountService就會發送Intent.ACTION_MEDIA_MOUNTED消息。
MountService由SystemServer啟動,我們簡單看看它的構造函數:
class MountService extends IMountService.Stub
implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
public MountService(Context context) {
.........
//與NetworkManagementService一樣,MountService也是靠NativeDaemonConnector與底層守護進程通信
//第一個參數傳入回調接口,第二參數指明通信的server端
mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25, null);
.........
//創建一個線程與server socket通信
mConnectorThread = new Thread(mConnector, VOLD_TAG);
..........
}
private void start() {
//NativeDaemonConnector是一個runnable對象,線程啟動後將調用其run方法
//在介紹Android7.0 數據業務長連接撥號過程時,我們提到過NetworkManagementService中NativeDaemonConnector連接netd的過程
//MountService中的過程是一致的,只是這次連接的是vold
mConnectorThread.start();
.........
}
}
從MountService的啟動情況來看,對於Vold進程而言,我們需要關注的就是MountService利用NativeDaemonConnector建立與”vold”的連接,使得Vold進程能夠與Android框架進行溝通了。
Android中的Service啟動後,基本上都是靠事件驅動的,因此無法按一個有序的流程進行全面的介紹,比較好的方式還是了解整體架構後,分析一個具體的示例。
因此接下來我們以設備插入為例,分析一下MountService的主要工作。
根據上文的分析,我們知道當設備插入後,Kernel發送消息是的NetlinkManager能夠收到Uevent。然後,NetlinkManager將會構造出NetlinkEvent,並通知VolumeManager進行處理。
在VolumeManager中,利用handleBlockEvent根據事件的類型進行相應的處理,我們截取設備添加時的處理代碼:
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
........
switch (evt->getAction()) {
case NetlinkEvent::Action::kAdd: {
//前文已述,DiskSource是配置VolumeManager時,讀取fstab文件得到的
for (auto source : mDiskSources) {
if (source->matches(eventPath)) {
.......
//創建設備對象
auto disk = new android::vold::Disk(eventPath, device,
source->getNickname(), flags);
disk->create();
mDisks.push_back(std::shared_ptr(disk));
break;
}
}
break;
}
........
}
我們看看Disk的代碼:
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 = StringPrintf("/sys/%s", eventPath.c_str());
mDevPath = StringPrintf("/dev/block/vold/%s", mId.c_str());
//在CreateDeviceNode中,利用mknod創建設備節點
CreateDeviceNode(mDevPath, mDevice);
}
status_t Disk::create() {
CHECK(!mCreated);
mCreated = true;
//注意這裡的notifyEvent
notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));
readMetadata();
readPartitions();
return OK;
}
status_t Disk::readMetadata() {
//讀取設備信息
...........
notifyEvent(ResponseCode::DiskSizeChanged, StringPrintf("%" PRIu64, mSize));
notifyEvent(ResponseCode::DiskLabelChanged, mLabel);
notifyEvent(ResponseCode::DiskSysPathChanged, mSysPath);
return OK;
}
status_t Disk::readPartitions() {
//讀取分取信息
.............
Table table = Table::kUnknown;
bool foundParts = false;
for (auto line : output) {
..........
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")) {
.........
dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);
if (table == Table::kMbr) {
........
createPublicVolume(partDevice);
........
} 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);
}
}
}
}
...............
}
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();
}
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;
}
上面列舉了創建Disk和Volume的代碼,注意到進行實際工作後,均會調用notifyEvent函數。
我們接下來就看看notifyEvent函數的用途:
void Disk::notifyEvent(int event, const std::string& value) {
//還記得麼?VolumeManager初始時指定其Broadcaster為CommandListener
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
StringPrintf("%s %s", getId().c_str(), value.c_str()).c_str(), false);
}
現在我們看看CommandListener的sendBroadcast函數(實際定義於父類的父類SocketListener中):
void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
SocketClientCollection safeList;
/* Add all active clients to the safe list first */
//這應該算一種好習慣吧
safeList.clear();
pthread_mutex_lock(&mClientsLock);
SocketClientCollection::iterator i;
//注意將SocketListener當前所有的SocketClient均加入到safeList,所以函數名才叫sendBroadcast
//當然,對於Vold進程而言,它的client只有MountService對應的socket
for (i = mClients->begin(); i != mClients->end(); ++i) {
SocketClient* c = *i;
c->incRef();
safeList.push_back(c);
}
pthread_mutex_unlock(&mClientsLock);
while (!safeList.empty()) {
/* Pop the first item from the list */
i = safeList.begin();
SocketClient* c = *i;
safeList.erase(i);
// broadcasts are unsolicited and should not include a cmd number
//前面提到過SocketClient的sendMsg底層就是靠socket通信方式
if (c->sendMsg(code, msg, addErrno, false)) {
SLOGW("Error sending broadcast (%s)", strerror(errno));
}
c->decRef();
}
}
從上面的代碼,我們知道將由MountService來處理socket中的數據。
前面我們已經知道,MountService創建NativeDaemonConnector來封裝socket相關的操作,在創建NativeDaemonConnector時需要傳入回調接口。當NativeDaemonConnector收到數據後,通過回調接口進行通知。
MountService繼承了INativeDaemonConnectorCallbacks,我們看看它的onEvent函數:
@Override
public boolean onEvent(int code, String raw, String[] cooked) {
synchronized (mLock) {
return onEventLocked(code, raw, cooked);
}
}
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;
}
...........
case VoldResponseCode.VOLUME_CREATED: {
final String id = cooked[1];
final int type = Integer.parseInt(cooked[2]);
final String diskId = TextUtils.nullIfEmpty(cooked[3]);
final String partGuid = TextUtils.nullIfEmpty(cooked[4]);
final DiskInfo disk = mDisks.get(diskId);
final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid);
mVolumes.put(id, vol);
onVolumeCreatedLocked(vol);
break;
}
...........
}
return true;
}
從上面的代碼可以看出,MountService收到DISK_CREATED消息後,僅會記錄DiskInfo;收到VOLUME_CREATED消息後,還需要調用onVolumeCreatedLocked函數作進一步地處理。
private void onVolumeCreatedLocked(VolumeInfo vol) {
.........
if (vol.type == VolumeInfo.TYPE_EMULATED) {
.......
} else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
//以public type為例
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
} else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
........
} else {
.......
}
}
class MountServiceHandler extends Handler {
public MountServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
........
final VolumeInfo vol = (VolumeInfo) msg.obj;
if (isMountDisallowed(vol)) {
Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy");
break;
}
try {
//利用NativeDaemonConnector中socket將消息發送給CommandListenr
mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
vol.mountUserId);
} catch (NativeDaemonConnectorException ignored) {
}
break;
........
}
}
}
注意消息重新發回到了CommandListener,根據前面的代碼的分析我們知道,在FrameworkListener中將利用dispatchCommand根據類型,調用不同Command的runCommand方法,此處將調用volumeCommand的運行方法:
int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
//解析參數
.........
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);
//調用volumebase的mount方法,實際調用其子類的doMount
int res = vol->mount();
if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {
vm->setPrimary(vol);
}
//執行成功後,將返回消息給MountService
return sendGenericOkFail(cli, res);
}
.........
}
我們跟進一下VolumeBase的mount函數:
void VolumeBase::setState(State state) {
mState = state;
//再次通知到MountService
notifyEvent(ResponseCode::VolumeStateChanged, StringPrintf("%d", mState));
}
在MountService的onEvent函數中,將再次處理VolumeStateChanged事件,實際上就是發送ACTION_MEDIA_MOUNTED廣播。
上述的整個過程略去了大量的細節,但看起來仍很瑣碎。不過,若是理解了前面介紹CommandListener時,分析的整個通信架構,那麼這些流程的大致方向是比較好理解的。
結束語
Vold進程的主要內容基本上就是這些,在實際的工作中大多數人應該不會接觸到這個進程。但是它整個架構是非常具有參考意義的,很清晰地闡釋了Android中的框架層、Native層以及Kernel是如何交互的。Android中還有許多重要部分也采用了類似的架構,比較明顯的就是netd。因此,以Vold入手進行分析,重在理解這種通信架構。
Android中的動畫
Animation 概述安裝中的動畫分為三種:補間動畫(Tween Animation),幀動畫(Frame Animation)和屬性動畫(Property Anima
android通過代碼控制ListView上下滾動
本文將介紹一種通過代碼控制ListView上下滾動的方法。 先上圖: 按下按鈕會觸發ListView滾動或停止。 實現該功能並不難,下面給出主要代碼MainAct
Android中用RxJava和ViewPager實現輪播圖
前言很多人要實現輪播圖都會想到使用ViewPager + Handler來完成輪播圖的效果。但是在RxJava快速發展的情況下,已經可以使用RxJava來代替Handle
教你輕松制作Android音樂播放器
欣賞一下我們清爽的界面吧~如果是只用activity來制作這樣的東西簡直是太小兒科了,此處我們當然用的是service首先我們先上service的代碼:1、如果我們要訪問