转载请标明出处:http://www.jianshu.com/users/183339cdc7ae/latest_articles
概述
前面几篇介绍了vold的框架和内部结构以及启动顺序,可以知道的是vold接受到消息后是如果工作的。这一片文章主要讲的是,vold中的接受上层消息的socket是如何注册的。
相关文章
Android5.0 vold-整体架构
Android5.0 vold-启动过程
Android5.0 vold-注册过程(下)
MountService
创建
由vold整体框架可以知道,vold能接受上层(MountService)发过来的信息,我们就从mountservice看起走,是在哪里注册的,先来看看构造方法.
这个NativeDaemonConnector类可以看出是继承自Runnable接口应用层(如:Setting)发送的命令会传给MountService,MountService中有一个线程来接受这个命令需要注意的是NativeDaemonConnector的第二个参数"vold",这里先记住这个参数的名字
public MountService(Context context) {
...
mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
null);
Thread thread = new Thread(mConnector, VOLD_TAG);
thread.start();
...
}
可以看出的是这个socket的名字就为vold.
NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl,
Looper looper) {
...
mSocket = socket;
...
}
因为之前执行了thread.start();所以我们再来看看NativeDaemonConnector的run方法
可以看到实例化了一个LocalSocket,这个LocalSocket是对unix socket操作的一个封装,可以理解为一个工具类,这里仅仅只是做了一个实例化的动作,还并没有创建socket
LocalSocketAddress对象也只是一个地址封装类,里面会封装socket的一个地址,查看代码可以知道的是这个地址名字就叫做“vold”,为前面mSocket的值,并且这个socket文件路径为: /dev/socket/vold
接下来是connect动作,这里面就会创建正真的socket并且指定地址为/dev/socket/vold在这个线程里面会监听由vold发过来的消息,然后再做后续的一些处理 [注:这里是vold发上来的信息]
public void run() {
...
while (true) {
...
listenToSocket();
...
}
}
private void listenToSocket() throws IOException {
LocalSocket socket = null;
...
socket = new LocalSocket();
LocalSocketAddress address = determineSocketAddress();
socket.connect(address);
...
int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
...
if (mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
event.getCode(), event.getRawEvent()))) {
...
}
}
写入
上面说了socket的创建和读取信息,接下来看是如何往socket里面写的
mountService中会有一些方法供app层调用,这里以加密手机文件为例(命令:cryptfs),最终会封装一些参数到NativeDaemonConnector的execute方法
这个mOutputStream对象也就是之前创建的socket中获取的OutputStream
execute方法最终会往socket中写入数据
public int encryptStorage(int type, String password) {
...
mConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
new SensitiveArg(toHex(password)));
...
}
public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)
throws NativeDaemonConnectorException {
...
mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));
...
}
Vold
创建
我们回顾一下init.rc里面启动vold的代码重点是socket这一行.
根据init的语法可以知道的是,这一行会生产一个名叫vold的socket文件在/dev/socket/目录下
service vold /system/bin/vold
class core
socket vold stream 0660 root mount
ioprio be 2
寻找
再回顾一下CommandListener的构造方法,这里传入的是vold一个字符串
这个vold会传入到SocketListener中,并赋值给mSocketName变量
CommandListener::CommandListener() :FrameworkListener("vold", true)
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :SocketListener(socketName, true, withSeq) {
根据前文我们可以知道,启动监听的时候会调用到startListener方法然后android_get_control_socket方法会根据mSocketName返回一个fd文件
int SocketListener::startListener(int backlog) {
...
if ((mSock = android_get_control_socket(mSocketName)) < 0) {
SLOGE("Obtaining file descriptor socket '%s' failed: %s",
mSocketName, strerror(errno));
return -1;
}
SLOGV("got mSock = %d for %s", mSock, mSocketName);
...
}
android_get_control_socket的定义是在system/core/include/cutils/sockets.h中具体的实现就不仔细看了,看注释可以知道的是,这里返回的fd文件会是init.rc里面创建的文件也就是/dev/socket/vold文件
/*
* android_get_control_socket - simple helper function to get the file
* descriptor of our init-managed Unix domain socket. `name' is the name of the
* socket, as given in init.rc. Returns -1 on error.
*
* This is inline and not in libcutils proper because we want to use this in
* third-party daemons with minimal modification.
*/
static inline int android_get_control_socket(const char *name)
{..}
总结
根据上文可以知道是
- init.rc在启动vold的时候,会先创建一个/dev/socket/vold的socket文件
- 然后native vold启动完成时,会监听这个/dev/socket/vold socket
- 再然后是我们的MountService的启动,启动的时候会先得到/dev/socket/vold的地址,然后connect这个地址
这样可以往这个socket里面写入值了
如果对socket不是很熟悉的童鞋可以看看 关于socket的基本操作