概述
前面幾篇介紹了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的基本操作