2025-05-23

前一篇/kf/201203/124633.html 講到SDCard unmout onEvent 發送socket 到框架層,接下來分析框架層得到數據後的流程。
 MoutService
當android 系統啟動時,system將MountService 添加到啟動服務裡面,而MountService 會開啟一個線程來運行NativeDaemonConnector,由它來監聽vold的消息,代碼:
 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG);
        mReady = false;
        Thread thread = new Thread(mConnector, VOLD_TAG);
        thread.start(); 

 該函數運行在MountService的構造函數裡面,而NativeDaemonConnector 本身就是繼承自Runnable。
NativeDaemonConnector
 Framework與vold 的通信是通過socket來實現的,不過該socket 由 android做瞭一個封裝,LocalSocket 實現的socket功能。
NativeDaecomConnector 位於framework/base/service/java/com/android/server目錄下, 監聽vold 的消息代碼在繼承自Runnable對象的run方法裡面 :
@Override
    public void run() {
        HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
        thread.start();
        mCallbackHandler = new Handler(thread.getLooper(), this);

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

 
NativeDaemonConnector 類實例化瞭一個LocalSocket來與vold 通信。LocalSocket 裡面有一個類LocalSocketImpl,該類部分是通過JNI實現的。
關於socket 內部如何通信,這個不是我們所關心的內容,因為如果要深入進去估計沒完沒瞭,有興趣的朋友可以參考源碼進入SocketListener查看:
建立連接
SocketListener::SocketListener
 
當main初始化CommandListener 後,會為socketName 傳入一個叫vold 的字符串
SocketListener::startListener
 
等待並接收連接請求
SocketListener::runListener
 
獲得命令參數
bool FrameworkListener::onDataAvailable
 
dispatchCommand 到相應的命令類,並返回一部分消息給上層
FrameworkListener::dispatchCommand
 再回過頭看NativeDaemonConnector 的listenToSocket,代碼中實例化瞭一個LocalSocketAddress的實例,並傳入一個叫"vold"字符串的socket 名稱,這與CommandListener中繼承瞭FrameworkListener時給的"vold"名稱是一致的,兩個socket名稱一致則可以互相進行通訊瞭,代碼如下:
private void listenToSocket() throws IOException {
        LocalSocket socket = null;
    Slog.w(TAG,String.format("NativeDaemonConnector—>listenToSocket:start"));
        try {
            socket = new LocalSocket();
            LocalSocketAddress address = new LocalSocketAddress(mSocket,
                    LocalSocketAddress.Namespace.RESERVED);

            socket.connect(address);

            InputStream inputStream = socket.getInputStream();
            mOutputStream = socket.getOutputStream();

            mCallbacks.onDaemonConnected();

            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);//解析socket 的數據並獲取event
                        if (LOCAL_LOGD) Slog.d(TAG, String.format("RCV <- {%s}", event));

                        String[] tokens = event.split(" ", 2);
                        try {
                            int code = Integer.parseInt(tokens[0]);

                            if (code >= ResponseCode.UnsolicitedInformational) {
                                mCallbackHandler.sendMessage(
                                        mCallbackHandler.obtainMessage(code, event));//發送消息給handler
                            } else {
                                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 (mDaemonLock) {
                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);
            }
        }
    } 

上面代碼,通過socket 並event 解析出來,並通handler 發送到handleMessage 中,當handleMessage接收到傳過來的消息時,會調用MountService 的onEvent 方法將code和event和sdcard 的狀態傳遞進去。代碼如下:
 public boolean handleMessage(Message msg) {
        String event = (String) msg.obj;
        Slog.w(TAG,String.format("NativeDaemonConnector—>handleMessage the event value is "+event));
        try {
            if (!mCallbacks.onEvent(msg.what, event, event.split(" "))) {
                Slog.w(TAG, String.format(
                        "Unhandled event '%s'", event));
            }
        } catch (Exception e) {
            Slog.e(TAG, String.format(
                    "Error handling '%s'", event), e);
        }
        return true;
    } 

 
 又回到MountService ,在onEvent裡面當接收到的code ==VoldResponseCode.VolumeBadRemoval時會調用updatePublicVolumeState,發送unmount改變的廣播,代碼如下:
else if (code == VoldResponseCode.VolumeBadRemoval) {
                if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
                /* Send the media unmounted event first */
                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
                action = Intent.ACTION_MEDIA_UNMOUNTED;

                if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
                updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
                action = Intent.ACTION_MEDIA_BAD_REMOVAL;}

 到這裡,進入updatePublicVolumeState看該函數裡的主要代碼:
synchronized (mListeners) {
            for (int i = mListeners.size() -1; i >= 0; i–) {
                MountServiceBinderListener bl = mListeners.get(i);
                try {
                    Slog.w(TAG,"MountService—>updatePublicVolumeState–>bl.mListener.onStorageStateChanged");
                    bl.mListener.onStorageStateChanged(path, oldState, state);
                } catch (RemoteException rex) {
                    Slog.e(TAG, "Listener dead");
                    mListeners.remove(i);
                } catch (Exception ex) {
                    Slog.e(TAG, "Listener failed", ex);
                }
            }
        }

 並且調用sendStorageIntent 方法將SDCard的Action:android.intent.action.MEDIA_BAD_REMOVAL 和dat:file:///mnt/sdcard 通過這個廣播發送出去,代碼如下:
 
    private void sendStorageIntent(String action, String path) {
        Intent intent = new Intent(action, Uri.parse("file://" + path));
        // add StorageVolume extra
        intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolumeMap.get(path));
        Slog.d(TAG, "sendStorageIntent " + intent);
        mContext.sendBroadcast(intent);
    }

 
再回到 updatePublicVolumeState ,調用瞭stateChange 後,將狀態為632的標識發送到NativeDaemonConnector 的handlemessage,當NativeDaemonConnector 發現SDCard的狀態發送改變時,比如unmount 的時候,從632(VolumeBadRemoval)變到605(VolumeStateChange)到onEvent,當onEvent再次得到請求時,進入判斷會直接執行notifyVolumeStateChange 函數,代碼如下:
if (code == VoldResponseCode.VolumeStateChange) {
            /*
             * One of the volumes we're managing has changed state.
             * Format: "NNN Volume <label> <path> state changed
             * from <old_#> (<old_str>) to <new_#> (<new_str>)"
             */
            notifyVolumeStateChange(
                    cooked[2], cooked[3], Integer.parseInt(cooked[7]),
                            Integer.parseInt(cooked[10]));
        } 

 
 notifyStateChange 會調用updatePublicVolumeState通知packageManger SDCard己經unmount.
 
 再回到Vold
由於vold 啟動文件一開始就啟動瞭CommandListener的runcommand由於socket 一直在通訊,當發現值改變後,進入以下代碼runCommand 方法裡面:
else if (!strcmp(argv[1], "unmount")) {
        if (argc < 3 || argc > 4 ||
           ((argc == 4 && strcmp(argv[3], "force")) &&
            (argc == 4 && strcmp(argv[3], "force_and_revert")))) {
            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount <path> [force|force_and_revert]", false);
            return 0;
        }

        bool force = false;
        bool revert = false;
        if (argc >= 4 && !strcmp(argv[3], "force")) {
            force = true;
        } else if (argc >= 4 && !strcmp(argv[3], "force_and_revert")) {
            force = true;
            revert = true;
        }
        rc = vm->unmountVolume(argv[2], force, revert);
    }  

 這時調用VolumeManage的unmoutVolume。該方法來源於Volume 的unmountVol,調用這個函數會unmount 三個掛載點,並同時調用setState通知框架unmount 成功,可以改變UI等一系列動作。
最後總結
MountService: 實現用於管理存儲設備的後臺服務
StorageManage:訪問MountService 接口,並向應用層提供接口
PackageMangeService:是用於管理系統中所有apk,當SDCard發生變化時,向應用層發送消息
NativeDaemonConnector:創建socket實現mountservice 和vold 的通信
 
可以這麼說:當vold 捕獲到uevent 事件,會將事件消息通知framework,framework 進行判斷,然後再下發執行命令。
 
粗糙圖
最後附一張比較粗糙的結構圖,時間較急,沒仔細畫好

  

摘自  Terry_龍  

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *