Android usb掛載識別學習筆記2 MountService

MountService
在android usb掛載分析----vold啓動,我們的vold模塊已經啓動了,通信的機制也已經建立起來了,接下來我們分析一下MountService的啓動,也就是我們FrameWork層的啓動,首先看下其大概流程:
MountService啓動流程
MountService的啓動在SystemServer.java中,這裏new 了一個MountService,並把service添加到了ServiceManager,有如下代碼:

private void startOtherServices() {
    ......
	if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
	    if (!disableStorage &&
	        !"0".equals(SystemProperties.get("system_init.startmountservice"))) {
	        try {
	            /*
	             * NotificationManagerService is dependant on MountService,
	             * (for media / usb notifications) so we must start MountService first.
	             */
	            Slog.i(TAG, "Mount Service");
	            mountService = new MountService(context);
	            ServiceManager.addService("mount", mountService);
	        } catch (Throwable e) {
	            reportWtf("starting Mount Service", e);
	        }
	    }
	}
	......
}

我們看下MountService的構造函數:

/**
     * Constructs a new MountService instance
     *
     * @param context  Binder context for this service
     */
    public MountService(Context context) {
        mContext = context;
 
        // XXX: This will go away soon in favor of IMountServiceObserver
        mPms = (PackageManagerService) ServiceManager.getService("package");//獲取包管理服務
 
        mContext.registerReceiver(mBroadcastReceiver,
                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);//註冊廣播接收器
 
        mHandlerThread = new HandlerThread("MountService");//處理消息
        mHandlerThread.start();
        mHandler = new MountServiceHandler(mHandlerThread.getLooper());
 
        // Add OBB Action Handler to MountService thread.
        mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
 
        /*
         * Vold does not run in the simulator, so pretend the connector thread
         * ran and did its thing.
         */
        if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
            mReady = true;
            mUmsEnabling = true;
            return;
        }
 
        /*
         * Create the connection to vold with a maximum queue of twice the
         * amount of containers we'd ever expect to have. This keeps an
         * "asec list" from blocking a thread repeatedly.
         */
        mConnector = new NativeDaemonConnector(this, "vold",
                PackageManagerService.MAX_CONTAINERS * 2, VOLD_TAG);
        mReady = false;
        Thread thread = new Thread(mConnector, VOLD_TAG);
        thread.start();
    }

後面new 了一個NativeDaemonConnector,注意這裏傳遞了一個"vold"字符串,跟我們在vold啓動的時候傳給CommandListener是一樣的。NativeDaemonConnector實現了Runnable接口

接下來調用 thread.start()啓動線程,我們看下它的run函數

public void run() {
 
        while (true) {
            try {
                listenToSocket();
            } catch (Exception e) {
                Slog.e(TAG, "Error in NativeDaemonConnector", e);
                SystemClock.sleep(5000);
            }
        }
    }

在循環中調用listenToSocket函數,看下這個函數

    private void listenToSocket() throws IOException {
        LocalSocket socket = null;
 
        try {
            socket = new LocalSocket();
            LocalSocketAddress address = new LocalSocketAddress(mSocket,   //這裏mSocket=“vold"
                    LocalSocketAddress.Namespace.RESERVED);              //注意這裏的RESERVED
 
            socket.connect(address);              //連接到vold模塊監聽的套接字處
            mCallbacks.onDaemonConnected();       //實現在MountService中
 
            InputStream inputStream = socket.getInputStream();
            mOutputStream = socket.getOutputStream();
 
            byte[] buffer = new byte[BUFFER_SIZE];
            int start = 0;
 
            while (true) {
                int count = inputStream.read(buffer, start, BUFFER_SIZE - start); //讀取消息
                if (count < 0) break;
 
                // Add our starting point to the count and reset the start.
                count += start;
                start = 0;
 
                for (int i = 0; i < count; i++) {
                    if (buffer[i] == 0) {
                        String event = new String(buffer, start, i - start);
                        if (LOCAL_LOGD) Slog.d(TAG, String.format("RCV <- {%s}", event));
 
                        String[] tokens = event.split(" ");
                        try {
                            int code = Integer.parseInt(tokens[0]);
 
                            if (code >= ResponseCode.UnsolicitedInformational) {
                                try {
                                    if (!mCallbacks.onEvent(code, event, tokens)) {//實現在MountService中
                                        Slog.w(TAG, String.format(
                                                "Unhandled event (%s)", event));
                                    }
                                } catch (Exception ex) {
                                    Slog.e(TAG, String.format(
                                            "Error handling '%s'", event), ex);
                                }
                            }
                            try {
                                mResponseQueue.put(event);
                            } catch (InterruptedException ex) {
                                Slog.e(TAG, "Failed to put response onto queue", ex);
                            }
                        } catch (NumberFormatException nfe) {
                            Slog.w(TAG, String.format("Bad msg (%s)", event));
                        }
                        start = i + 1;
                    }
                }
 
                // We should end at the amount we read. If not, compact then
                // buffer and read again.
                if (start != count) {
                    final int remaining = BUFFER_SIZE - start;
                    System.arraycopy(buffer, start, buffer, 0, remaining);
                    start = remaining;
                } else {
                    start = 0;
                }
            }
        } catch (IOException ex) {
            Slog.e(TAG, "Communications error", ex);
            throw ex;
        } finally {
            synchronized (this) {
                if (mOutputStream != null) {
                    try {
                        mOutputStream.close();
                    } catch (IOException e) {
                        Slog.w(TAG, "Failed closing output stream", e);
                    }
                    mOutputStream = null;
                }
            }
 
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException ex) {
                Slog.w(TAG, "Failed closing socket", ex);
            }
        }
    }

onDaemonConnected的實現在MountServices中,將向下下發volume list消息 獲取到了磁盤的標籤,掛載點與狀態,調用connect函數連接到vold模塊,connetc最終調用native函數connectLocal進行連接工作,我們看下他的jni層代碼,最後調用的:

int socket_local_client_connect(int fd, const char *name, int namespaceId, 
        int type)
{
    struct sockaddr_un addr;
    socklen_t alen;
    size_t namelen;
    int err;
 
    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
 
    if (err < 0) {
        goto error;
    }
 
    if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
        goto error;
    }
 
    return fd;
 
error:
    return -1;
}

我們再跟進socket_make_sockaddr_un函數,這時namespaceId傳的ANDROID_SOCKET_NAMESPACE_RESERVED,所以會執行下面幾句:

 case ANDROID_SOCKET_NAMESPACE_RESERVED:
            namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
            /* unix_path_max appears to be missing on linux */
            if (namelen > sizeof(*p_addr) 
                    - offsetof(struct sockaddr_un, sun_path) - 1) {
                goto error;
            }
 
            strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);  //  ANDROID_RESERVED_SOCKET_PREFIX="/dev/socket/"
            strcat(p_addr->sun_path, name);
        break;

注意在前面 connect 函數中的套接字的構造,使用了AF_LOCAL:

int socket_local_client(const char *name, int namespaceId, int type)
{
    int s;
 
    s = socket(AF_LOCAL, type, 0);
    if(s < 0) return -1;
 
    if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
        close(s);
        return -1;
    }
 
    return s;
}

這樣,就建立了一條從FrameWork層到vold層的通信鏈路,後面FrameWork層就等待Vold發送消息過來了。。。

FrameWork層的通信也ok了,就可以等待U盤掛載了。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章