Android5.0 vold-註冊過程(上)

Hly_Coder發表於2016-11-30

概述

前面幾篇介紹了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)
{..}複製程式碼

總結

根據上文可以知道是

  1. init.rc在啟動vold的時候,會先建立一個/dev/socket/vold的socket檔案
  2. 然後native vold啟動完成時,會監聽這個/dev/socket/vold socket
  3. 再然後是我們的MountService的啟動,啟動的時候會先得到/dev/socket/vold的地址,然後connect這個地址

這樣可以往這個socket裡面寫入值了
如果對socket不是很熟悉的童鞋可以看看 關於socket的基本操作

相關文章