转:来源于网络,忘记地方了,有知道地方的,请提醒下我
1. Vold 简介
1.1 udev的由来udev 是 Linux2.6 内核里的一个功能,它替代了原来的 devfs,成为当前 Linux 预设的设备管理工具。udev 以守护进程的形式运行,通过侦听内核发出来的 uevent 来管理 /dev目录下的设备文件。不像之前的设备管理工具,udev 在用户空间 (user space) 运行,而不在内核空间 (kernel space) 运行。
devfs 存在的主要的问题是它处理设备检测、创建和命名的方式,其中设备节点的命名可能是最严重的问题。一般可接受的方式是,如果设备名 是可配置的,那么设备命名策略应该由系统管理员决定,而不是由某些开发者强制规定。devfs 文件系统还存在竞争条件(race conditions) 的问题,这是它天生的设计缺陷,不对内核做彻底的修改就无法修正这个问题。所以udef应运而生。
udev能够实现所有devfs实现的功能。具有以下优点:
1) dynamic replacement for /dev。作为devfs的替代者,传统的devfs不能动态分配major和minor的值,而major和minor非常有限,很快就会用完了。 udev能够像DHCP动态分配IP地址一样去动态分配major和minor。
2) device naming。提供设备命名持久化的机制。传统设备命名方式不具直观性,像/dev/hda1这样的名字肯定没有boot_disk这样的名字直观。udev能够像DNS解析域名一样去给设备指定一个有意义的名称。
3) API to access info about current system devices 。提供了一组易用的API去操作sysfs,避免重复实现同样的代码,
但udev运行在用户模式中,而devfs运行在内核中。udev只支持linux-2.6内核,因为udev严重依赖于sysfs文件系统提供的信息,而sysfs文件系统只在linux-2.6内核中才有。
1.2 Vold的产生
Vold的全称是Volume Daemon。在android中,取代udev的是vold,我们这里不去过多的讨论为什么android不继续使用udev,但要知道vold的机制和udev是一样的,理解了udev,也就理解了vold。android一出生就没有尊守传统linux的许多标准,当然也不能指望udev能很好的服务于android。android小区的选择是别起炉灶,为android定做一套udev,这就是vold了。无论是udev还是vold,都是基于sysfs的,sysfs为内核与用户层的通讯提供了一种全新的方式,并将这种方式加以规范。kernel层能检测到有新的设备接入,并能为之加载相应的驱动,但如何通知用户层呢?这就是sysfs的工作,内核中的sysfs机制要求当有新的驱动加载时给用户层发送相应的event.但这些event只尽告知的义务,具体怎么处理,这就是vold的事了。对于用户层而言,我们无需关心sysfs的
细节,只要知道sysfs 能向用户层提供什么就行了。首先,我们要知道如何接收来自内核的
event.Netlink socket 大家应该不会陌生吧,socket 这套东西不仅能用于网络间的通讯,
也用能用于进程间的通讯,像这种内核态与用户沟通的活,自然也少不了它。关于这个部分,
我们将在Vold 通信仲介绍。关于Vold 的主要作用,如下图所示:
NativeDaemonConnector
MountService Java空间
C++空间
Volume Daemon
Linux Kernel
sysfs
uevent Function call
庫函數
onEvent Command
command onEvent
Volume Daemon 简介
Android 的volume 服务主要是用来管理usb/sd 卡等外部存储设备。平台可以对外部存
储设备进行操作和轮询状态,当外部存储设备状态发生变化时,volume 服务也会实时报
告平台。
2. Vold 功能分析
2.1. Vold 启动
Volume Daemon 实在android init 进程中启动的(关于android 启动有其他同事介绍)。在init
进程中将解析init.rc 文件。在该文件中有启动Vold 的配置。见下图:
在这里将启动Vold Daemon,并且创建一个socket。改socket 主要是为了与framework 层通
信。
2.2 Vold 内部架构
NetLinkManager
CommandListener
Volume
DirectVolume
AutoVolume
SocketListener
VolumeManager
Vold(Volume Daemon)的内部框架如上图所示。Vold 处理过程大致分为三步:创建连
结、引导和事件处理。下面将结合上图对vold 工作流程进行详细分析。
2.2.1. 创建连结
在vold作为一个守护进程,一方面接受驱动的信息,并把信息传给应用层;另一方面接受上层的命令并完成相应。所以这里的连结一共有两条:
1) vold socket: 负责vold与framework层的信息传递;
2) 接受sysfs uevent的socket: 负责接受kernel的信息;
Vold socket是在init进程启动Volume Daemon时创建的,这是一个用于和framework层通信的socket,在android系统中叫做Localsocket,framework曾通过JNI机制调用C/C++空间函数,具体过程如下图:
在framework层的NativeDaemonConnector类中调用下面代码
其中mSocket值为“vold”表示要获得socket的名字,该socket上上所述,是在vold被init的时候创建的,并且保存为全局变量。在socket.connect()中调用LocalSocketImpl.create_native()方法。改方法通过一个函数对应表,调用相应的C/C++函数。该表如下所示:
关于JNI由其他同事详细介绍,这里不展开,只要知道用Java JNI机制调用的是C/C++函数。这个framework层和Vold中分别持有同一个socket实例,这样上层通信就建立起来了。
而与内核通信的socket是在vold main函数中创建的。在main函数中,vold将创建两个单例,VolumeManager和NetLinkManager,与内核通信的socket就在其中创建。这样Volume Daemon与Kernel、framework层的通信框架就建立了。关于Volume Daemon的内部通行将在下一节中详细解释。
2.2.2. 引导
这里主要是在vold启动时,对现有外设存储设备的处理。首先,要载入并解析vold.fstab。vold.fstab如下图所示:
图中红色部分标记可配置的部分。我们在Porting的时候可以设置,系统可以挂载USB Storage 端口的数目。配置格式为:
dev_mount命令 标签 挂载点 子分区 设备在sysfs文件系统下的路径(可多个)
以Android X86 froyo中的vold.fstab为例,它仅仅为系统配置了一个外接USB Storage埠,如上图红色部分所示,其意义对应下表。
dev_mount命令:
dev_mount
<label>标签
sdcard
<mount_point>挂载点:
/mnt/sdcard
<part>子分区个数:
auto
<sysfs_path1...>设备在sysfs文件系统下的路径(可多个):
/devices/platform/goldfish_mmc.0
需要注意的是:
1) 子分区的数目可以为auto,表示只有一个子分区。子分区数目也可以为任意大于0的一个整数。
2) 个参数间不能有空格,应该以tab制表符为参数的间隔,原因是android对vold.fstab的解析是以”\t”为标识,从而得到各个参数。
如果vold.fatab读取成功,个参数经过验证无误。那个将创建DirectVolume
如果vold.fstab档不存在或者打开档失败,vold将调用:
"/proc/cmdline"fp = fopen("/proc/cmdline", "r")读取Linux内核启动参数。见下图:
如果在Linux内核参数中存在SDCARD(该参数辨识系统SDCARD设备文件的路径),那么将取出该参数值创建AutoVolume。如果不存在SDCARD参数,那么将不创建AutoVolume。见下图:
2.2.3. 事件处理
这里通过对两个连结的监听,完成对动态事件的处理,以及对上层应用操作的响应。
我们来具体分析一下代码过程。
1)kernel发出uevent
NetLinkManager检测kernel发出的uevent,经过解析后,调用NetLinkHandler::onEvent()。如下图所示:
对于不同的时间类型分别处理。“block”事件由VolumeManager的handleBlockEvent(evt)处理,由多态性,最终调用的是AutoVolume或者DirectVolume的handleBlockEvent(evt)。“switch”事件由VolumeManager处理。忽略“battery”和“power_supply”事件。
这里做一下解释block主要指对volume的一些操作的事件,如mount,unmonut,createAsec等。Switch主要指volume的connect或者disconnect。
最终根据相关操作,改变设备参数(如设备类型,挂载点等等)。通过CommandListener告知framework层。
2)framework层发出命令
和上面过程正好相反,CommandListener检测到framework层的命令,调用VolumeManager的函数,VolumeManager遍历Volume清单找出对应的volume,调用volume的函数。而volume类中的相关调用,最终调用到Linux函数,完成相关操作。
3. Vold通信
3.1实现框架图
在vold作为一个守护进程,一方面接受驱动的信息,并把信息传给应用层;另一方面接收上层的命令并完成相应操作。所以这里的连结一共有两条: (1)vold socket: 负责vold与应用层的信息传递;
(2)访问vold的socket:负责vold与底层的信息传递;
这两个连结都是在进程的一开始完成创建的。
3.2 Uevent简介
uevent由内核发出,通过netlink sokect来传递给vold,在kobject被创建的时候,就会发生uevent的传递。对于未传递的uevent,会在kset下产生uevent文件,这是供用户态触发uevent使用的,通过向uevent档写入action(add,remove等),可以触发一个uevent,这些uevent可以被vold捕获,从而完成未完成的vold处理。在系统启动的时候,vold未启动的时候,这些uevent写入了uevent檔,vold启动后,会扫描sys目录查找uevent,然后触发它们,来完成之前未完成的事宜。uevent文件的内容,就是uevent事件的数据。
3.3 NetLink 简介
NetLink socket机制
Netlink socket,socket不仅能用于网络间的通讯,也用能用于进程间的通讯,像这种内核态
与用户沟通的活,自然也少不了它。
在 Linux 2.4 版以后版本的内核中,几乎全部的中断过程和使用者态进程的通信都是使用 netlink 套接字实现的。netlink 套接字的最大特点是对中断过程的支持,他在内核空间接收用户空间数据时不再需要用户自行启动一个内核线程,而是通过另一个软中断调用用户事先指定的接收函数。工作原理如图。
用户空间 用户态应用使用标准的socket和内核通讯,标准的socket API 的函数,socket(), bind(), sendmsg(), recvmsg() 和 close()非常容易地应用到 netlink socket。
为了创建一个 netlink socket,使用者需要使用如下参数调用 socket() socket(AF_NETLINK, SOCK_RAW, netlink_type) netlink对应的协议簇是 AF_NETLINK,第二个参数必须是SOCK_RAW或SOCK_DGRAM, 第三个参数指定netlink协议类型,他能是个自定义的类型,也能使用内核预定义的类型:
…..
#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ 同样地,socket函数返回的套接字,能交给bind等函数调用: static int skfd; skfd = socket(PF_NETLINK, SOCK_RAW, NL_IMP2); bind函数需要绑定协议地址,netlink的socket地址使用struct sockaddr_nl架构: struct sockaddr_nl { sa_family_t nl_family; unsigned short nl_pad; __u32 nl_pid; __u32 nl_groups; };
成员 nl_family为协议簇 AF_NETLINK,成员 nl_pad 当前没有使用,因此要总是设置为 0,
成员 nl_pid 为接收或发送消息的进程的 ID,如果希望内核处理消息或多播消息,就把该字段设置为 0,否则设置为处理消息的进程 ID。成员 nl_groups 用于指定多播组,bind 函数用于把调用进程加入到该字段指定的多播组,如果设置为 0,表示调用者不加入所有多播组:struct sockaddr_nl local; memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; local.nl_pid = getpid(); /*设置pid为自己的pid值*/ local.nl_groups = 0; /*绑定套接字*/ if(bind(skfd, (struct sockaddr*)&local, sizeof(local)) != 0) {
printf("bind() error\n"); return -1; }
用户空间能调用send函数簇向内核发送消息,如sendto、sendmsg等,同样地,也能使用struct sockaddr_nl来描述一个对端地址,以待send函数来调用,和本地地址稍不同的是,因为对端为内核,所以nl_pid成员需要设置为0: struct sockaddr_nl kpeer; memset(&kpeer, 0, sizeof(kpeer)); kpeer.nl_family = AF_NETLINK; kpeer.nl_pid = 0; kpeer.nl_groups = 0;
rcvlen = recvfrom(skfd, &info, sizeof(struct u_packet_info), 0, (struct sockaddr*)&kpeer, &kpeerlen); /*处理接收到的数据*/ }
同样地,函数close用于关闭打开的netlink socket.
3.4主要代码介绍
代码位于目录
System\vold
system\core\include\sysutils
system\core\libsysutils\src
在vold作为一个守护进程,一方面接受驱动的信息,并把信息传给应用层;另一方面接受上层的命令并完成相应操作。 所以这里的连结一共有两条: (1)vold socket: 负责vold与应用层的信息传递;
(2)访问Vold的socket: 负责vold与底层的信息传递;
这两个连结都是在进程的一开始完成创建的上面。
3.4.1初始化
Main.cpp
VolumeManager, CommandListener, NetlinkManager 都是在main()函数里面初始化的。
其中VolumeManager,NetlinkManager内部采用单例模式。
(1) Class NetlinkManager 主要是创建于内核通信的socket,接收来自底层的信息,然后传交给VolumeManager处理。
(2) class CommandListener主要收到上层 MountService通过doMountVolume发来的命令,分析后,转交给VolumeManager处理;VolumeManager处理信息后,或报告给上层MountService,或交给volume执行具体操作。 int main() { VolumeManager *vm; CommandListener *cl; NetlinkManager *nm; /* Create our singleton managers */ if (!(vm = VolumeManager::Instance())) { SLOGE("Unable to create VolumeManager"); exit(1); }; if (!(nm = NetlinkManager::Instance())) { SLOGE("Unable to create NetlinkManager"); exit(1); }; cl = new CommandListener(); vm->setBroadcaster((SocketListener *) cl); nm->setBroadcaster((SocketListener *) cl); if (vm->start()) { SLOGE("Unable to start VolumeManager (%s)", strerror(errno)); exit(1); } if (nm->start()) { SLOGE("Unable to start NetlinkManager (%s)", strerror(errno)); exit(1); } coldboot("/sys/block"); …….. if (cl->startListener()) { SLOGE("Unable to start CommandListener (%s)", strerror(errno));
exit(1); } }
3.4.2处理来自内核的uevent
建立socket连接
NetlinkManager.cpp
int socket(int domain, int type, int protocol);
domain指明所使用的协议族,PF_NETLINK表示Kernel和用户空间的通信。type参数指定socket的类型:SOCK_STREAM 或SOCK_DGRAM,Socket接口还定义了原始Socket(SOCK_RAW) 内核不区分SOCK_DGRAM和SOCK_RAW。允许程序使用低层协议;protocol通常赋值"0"。Socket()调用返回一个整型socket描述符,你可以在后面的调用使用它。 Socket描述符是一个指向内部数据结构的指针,它指向描述符表入口。调用Socket函数时,socket执行体将建立一个Socket,实际上"建立一个Socket"意味着为一个Socket数据结构分配存储空间。 Socket执行体为你管理描述符表。两个网络程序之间的一个网络连接包括五种信息:通信协议、本地协议地址、本地主机端口、远程主机地址和远程协议端口。
int NetlinkManager::start() { struct sockaddr_nl nladdr; int sz = 64 * 1024; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = getpid(); nladdr.nl_groups = 0xffffffff; //NETLINK_KOBJECT_UEVENT /*Kernel messages to userspace*/ if ((mSock = socket(PF_NETLINK, SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) { SLOGE("Unable to create uevent socket: %s", strerror(errno)); return -1; } if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) { SLOGE("Unable to set uevent socket options: %s", strerror(errno)); return -1; } // bind函数需要绑定协议地址,netlink的socket地址使用struct sockaddr_nl架构 if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) { SLOGE("Unable to bind uevent socket: %s", strerror(errno)); return -1; } mHandler = new NetlinkHandler(mSock); if (mHandler->start()) { SLOGE("Unable to start NetlinkHandler: %s", strerror(errno)); return -1; } return 0; }
接受来自内核的uevent
Class commandlistener 在main()初始化,之后开始开始监听,会开一个线程不停的监听来自内核的消息。
socketListener.cpp void SocketListener::runListener() { while(1) { …. do { pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { nt fd = (*it)->getSocket(); if (FD_ISSET(fd, &read_fds)) { pthread_mutex_unlock(&mClientsLock); if (!onDataAvailable(*it)) { close(fd); pthread_mutex_lock(&mClientsLock); delete *it; it = mClients->erase(it); pthread_mutex_unlock(&mClientsLock); } FD_CLR(fd, &read_fds); continue; } } pthread_mutex_unlock(&mClientsLock); } while (0); } }
处理
NetlinkListener里面实现onDataAvailable,接受来自内核的消息,然后由NetlinkEvent来解析. bool NetlinkListener::onDataAvailable(SocketClient *cli) { int socket = cli->getSocket(); int count; if ((count = recv(socket, mBuffer, sizeof(mBuffer), 0)) < 0) { SLOGE("recv failed (%s)", strerror(errno)); return false; }
NetlinkEvent *evt = new NetlinkEvent(); if (!evt->decode(mBuffer, count)) { SLOGE("Error decoding NetlinkEvent"); goto out; } onEvent(evt); out: delete evt; return true; }
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); } else if (!strcmp(subsys, "switch")) { vm->handleSwitchEvent(evt); } else if (!strcmp(subsys, "battery")) { } else if (!strcmp(subsys, "power_supply")) { } }
3.4.3处理来自framework层发出的command
class CommandListener主要收到上层 MountService通过doMountVolume发来的命令,分析后,转交给VolumeManager处理;VolumeManager处理信息后,或报告给上层MountService,或交给volume执行具体操作。
注册及初始化 CommandListener::CommandListener() : FrameworkListener("vold") { registerCmd(new DumpCmd()); registerCmd(new VolumeCmd()); registerCmd(new AsecCmd()); registerCmd(new ShareCmd()); registerCmd(new StorageCmd()); registerCmd(new XwarpCmd()); }
在FrameworkListener会有一个collection,把注册进来的command加入列表。
当收到上层传来的命令后解析命令,分发到不同的命令类处理。
void FrameworkListener::registerCmd(FrameworkCommand *cmd) { mCommands->push_back(cmd); }
VoldCommand主要是实现对runcommand动作的封装,在 FrameworkListener会根据收到的消息选择相应的派生类。
NavtiveDaemonConnector
Framework与vold的通信时通过socket来实现的,不过该socket有android做了一个封装,LocalSocket实现的socket的功能。
NavtiveDaemonConnector位于frameworks\base\services\java\com\android\server目录下,他会开启一个线程不停的监听来自vold的消息。 public void run() { while (true) { try { listenToSocket(); } catch (Exception e) { Slog.e(TAG, "Error in NativeDaemonConnector", e); SystemClock.sleep(5000); } } }
NavtiveDaemonConnector 类实例化一个LocalSocket来与vold通信。LocalSocket里面有一个类LocalSocketImpl ,该类部分函数时通过JNI实现的。
建立连接 SocketListener::SocketListener(const char *socketName, bool listen) { mListen = listen; mSocketName = socketName; mSock = -1; pthread_mutex_init(&mClientsLock, NULL); mClients = new SocketClientCollection(); }
初始化CommandListener后,此时socketName为传进来的参数“vold” int SocketListener::startListener() { if (!mSocketName && mSock == -1) { SLOGE("Failed to start unbound listener"); errno = EINVAL; return -1; } else if (mSocketName) { if ((mSock = android_get_control_socket(mSocketName)) < 0) { SLOGE("Obtaining file descriptor socket '%s' failed: %s", mSocketName, strerror(errno)); return -1; } } if (mListen && listen(mSock, 4) < 0) { SLOGE("Unable to listen on socket (%s)", strerror(errno)); return -1; } else if (!mListen) mClients->push_back(new SocketClient(mSock));
if (pipe(mCtrlPipe)) { SLOGE("pipe failed (%s)", strerror(errno)); return -1; } if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) { SLOGE("pthread_create (%s)", strerror(errno)); return -1; } return 0; }
等待并接收连接请求 void SocketListener::runListener() { while(1) { ….. if ((c = accept(mSock, &addr, &alen)) < 0) { SLOGE("accept failed (%s)", strerror(errno)); sleep(1); continue; } pthread_mutex_lock(&mClientsLock); mClients->push_back(new SocketClient(c)); pthread_mutex_unlock(&mClientsLock); } do { …….. if (!onDataAvailable(*it)) { close(fd); pthread_mutex_lock(&mClientsLock); delete *it; it = mClients->erase(it); pthread_mutex_unlock(&mClientsLock); } FD_CLR(fd, &read_fds); continue; } } pthread_mutex_unlock(&mClientsLock); } while (0); } }
获得命令参数 bool FrameworkListener::onDataAvailable(SocketClient *c) {
char buffer[255]; int len; if ((len = read(c->getSocket(), buffer, sizeof(buffer) -1)) < 0) { SLOGE("read() failed (%s)", strerror(errno)); return errno; } else if (!len) return false; int offset = 0; int i; for (i = 0; i < len; i++) { if (buffer[i] == '\0') { dispatchCommand(c, buffer + offset); offset = i + 1; } } return true; }
dispatchCommand 到相应的命令类,并返回一部分消息给上层 void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { …. cli->sendMsg(500, "Unsupported escape sequence", false); ……. if (c->runCommand(cli, argc, argv)) { return; }
向framework层回馈信息。 int SocketClient::sendMsg(int code, const char *msg, bool addErrno) { char *buf; if (addErrno) { buf = (char *) alloca(strlen(msg) + strlen(strerror(errno)) + 8); sprintf(buf, "%.3d %s (%s)", code, msg, strerror(errno)); } else { buf = (char *) alloca(strlen(msg) + strlen("XXX ")); sprintf(buf, "%.3d %s", code, msg); } eturn sendMsg(buf); } …… while(brtw) { if ((rc = write(mSocket,p, brtw)) < 0) { SLOGW("Unable to send msg '%s' (%s)", msg, strerror(errno)); pthread_mutex_unlock(&mWriteMutex);
return -1; } else if (!rc) { SLOGW("0 length write :("); errno = EIO; pthread_mutex_unlock(&mWriteMutex); return -1; } p += rc; brtw -= rc; } pthread_mutex_unlock(&mWriteMutex); return 0; }
4.Usb Storage Framework 分析
在Framework层,对存储设备进行管理和操作的类主要包括:MountService,StorageManager,PackageManagerService,PackageHelper,NativeDaemonConnector,它们之间的调用关系如上图所示。其中:
MountService实现用于管理存储设备的后台服务,实现了对存储设备的操作,应用程序可以通过StorageManager来访问这个服务。
StorageManager相当于系统存储设备管理服务的接口,它向上提供了一些方法供应用程序调用,比如查看Usb Mass Storage状态,设置Usb Mass Storage Enabled/Disabled等,然后将用户的请求转发到MountService中进行处理。
PackageManagerService是用于管理系统中所有apk的服务。
PackageHelper是一个辅助类,帮助PackageManagerService调用MountService中的方法。
NativeDaemonConnector:这个类中会创建一个sokect,用来实现MountService同Vold的通信。
MountService
StorageManager
NativeDaemonConnector
Vold
PackageManagerService
PackageHelper
应用程序可通过StorageManager调用MountsService中的方法对Usb Mass Storage进行操作
当Volume状态变更,或者Usb Mass Storage连接状态变更时,MountService会通知StorageManager
MountsService中有一个NativeDaemonConnect的实例,用来说明同Vold之间进行通信,发送命令
两者之间通过socket进行通信
当volume状态变更为挂载或卸除时,会通知PackageManagerService
PackageManagerService通过PackageHelper调用MountService中同secure container相关方法
MountsService中开启了一个线程,会查看是否有来自Vold的事件,并调用MountService的方法进行处理。
4.1MountService功能介绍:
MountService是一个服务类,在ServiceManager中注册为系统服务,提供对外部存储设备的管理、查询等服务,并在存储设备状态变更时发出通知。MountService起到了一个承上启下的作用,向上公开方法供上层对存储设备进行操作(enable/disable/mount…),并在存储设备状态变更时发出通知。向下接收Vold发来的事件(设备状态变更,设备插入,设备移除等),同时也会将命令发送给Vold,进行更底层的操作。
MountService中的方法如下表所示:
Public function void onDaemonConnected()
boolean
onEvent(int code, String raw, String[] cooked) void registerListener(IMountServiceListener listener) Registers an IMountServiceListener for receiving async notifications.
void
unregisterListener(IMountServiceListener listener)
Unregisters an IMountServiceListener void shutdown(final IMountShutdownObserver observer) Shuts down the MountService and gracefully unmounts all external media. Invokes call back once the shutdown is complete.
boolean
isUsbMassStorageConnected()
Returns true if a USB mass storage host is connected void setUsbMassStorageEnabled(boolean enable) Enables / disables USB mass storage. The caller should check actual status of enabling/disabling mass storage via StorageEventListener.
boolean
isUsbMassStorageEnabled()
Returns true if a USB mass storage host is enabled (media is shared) String getVolumeState(String mountPoint) Gets the state of a volume via its mount point.
int
mountVolume(String path)
Mount external storage at given mount point.
Returns an int consistent with MountServiceResultCode void unmountVolume(String path, boolean force) Safely unmount external storage at given mount point. The unmount is an asynchronous operation. Applications should register StorageEventListener for storage related status changes.
int
formatVolume(String path)
Format external storage given a mount point.
Returns an int consistent with MountServiceResultCode int [] getStorageUsers(String path) Returns an array of pids with open files on the specified path.
String[]
getSecureContainerList()
Gets an Array of currently known secure container IDs int createSecureContainer(String id, int sizeMb, String fstype, String key, int ownerUid) Creates a secure container with the specified parameters. Returns an int consistent with MountServiceResultCode
int
finalizeSecureContainer(String id)
Finalize a container which has just been created and populated.
After finalization, the container is immutable.
Returns an int consistent with MountServiceResultCode int destroySecureContainer(String id, boolean force) Destroy a secure container, and free up all resources associated with it. NOTE: Ensure all references are released prior to deleting. Returns an int consistent with MountServiceResultCode
int
mountSecureContainer(String id, String key, int ownerUid)
Mount a secure container with the specified key and owner UID.
Returns an int consistent with MountServiceResultCode int unmountSecureContainer(String id, boolean force) Unount a secure container. Returns an int consistent with MountServiceResultCode
boolean
isSecureContainerMounted(String id)
Returns true if the specified container is mounted int renameSecureContainer(String oldId, String newId) Rename an unmounted secure container. Returns an int consistent with MountServiceResultCode
String
getSecureContainerPath(String id)
Returns the filesystem path of a mounted secure container. void finishMediaUpdate() Call into MountService by PackageManager to notify that its done processing the media status update request.
Package function void waitForReady()
void
doShareUnshareVolume(String path, String method, boolean enable)
发送volume share/unshared命令 void updatePublicVolumeState(String path, String state) 通知更新存储设备状态
void
notifyVolumeStateChange(String label, String path, int oldState, int newState)
发出存储设备状态变更广播,并调用update函数 boolean doGetShareMethodAvailable(String method)
发送share status命令,如果返回response为Available则返回true
int
doMountVolume(String path)
发送 volume mount 命令 int doUnmountVolume(String path, boolean force)
int
doFormatVolume(String path)
发送 volume format 命令 boolean doGetVolumeShared(String path, String method) 发送volume shared命令,返回response为enabled则返回true
void
notifyShareAvailabilityChange(String method, final boolean avail)
通知UMS连接状态变更,并发出广播 void sendUmsIntent(boolean c)
void
validatePermission(String perm) boolean getUmsEnabling()
void
setUmsEnabling(boolean enable) void warnOnNotMounted()
4.2MountService同StroageManager的交互
MountService是运行在SystemService这个进程中,所以上层应用无法直接访问,StorageManager就是提供给应用层来访问存储服务的,它通过Binder机制与MountService所在进程进行通信,将使用者的请求转发进MountService中进行处理。目前StorageManager中支持的方法有enableUsbMassStorage,disableUsbMassStorage,isUsbMassStorageConnected,isUsbMassStorageConnected。在StorageManager的构造函数中,还通过调用MountService中的registerListener函数来注册listener到MountService,同时,它自己也提供了registerListener函数供其它应用来注册listener,这样,当MountService知道存储设备状态变更时,会调用StorageManager中listener的方法,而StorageManager又会继续回调上去,上层应用也就可以做相应的操作,比如图示的更新等。
4.3MountService同Vold的交互
MountService通过NativeDaemonConnector来和Vold传递信息,NativeDaemonConnector是一个继承自Runnable的类,它的构造函数中需要传入一个回调接口,在它的run函数里会创建socket并一直监听,当收到需要上层处理的信息时,会调用传入接口中的onEvent。NativeDaemonConnector中也提供了doCommond方法向socket另一端发送信息。
MountService会在构造函数中创建一个NativeDaemonConnector的实例,把自身作为参数传递进去用以回调,然后开启这个线程,这样就创建了同Vold通信的socket并监听来自vold的消息。这些消息包括:VolumeStateChange,ShareAvailabilityChange,VolumeDiskInserted,VolumeDiskRemoved,VolumeBadRemoval,收到消息后回调onEvent做处理。MountService也能通过调用doCommond向Vold发送消息。
4.4MountService同PackageManagerService的交互
PackageManagerService是一个用于管理设备中所有apk的服务。当外部存储设备被挂载或卸除时,会通知PackagemanagerService更新状态。然后PackageManagerService通过PackageHelper调用MountService中secure container相关方法。