Android SDCard UnMounted 流程分析(三)

 

前篇地址

Android SDCard UnMounted 流程分析(一)

Android SDCard UnMounted 流程分析(二)

 

 前一篇講到SDCard unmout onEvent 發送socket 到框架層,接下來分析框架層獲得數據後的流程。html

 MoutServicejava

當android 系統啓動時,system將MountService 添加到啓動服務裏面,而MountService 會開啓一個線程來運行NativeDaemonConnector,由它來監聽vold的消息,代碼:android

 mConnector =  new NativeDaemonConnector( this" vold ", MAX_CONTAINERS *  2, VOLD_TAG);
        mReady =  false;
        Thread thread =  new Thread(mConnector, VOLD_TAG);
        thread.start(); 

 該函數運行在MountService的構造函數裏面,而NativeDaemonConnector 自己就是繼承自Runnable。框架

NativeDaemonConnectorsocket

 Framework與vold 的通訊是經過socket來實現的,不過該socket 由 android作了一個封裝,LocalSocket 實現的socket功能。ide

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實現的。oop

關於socket 內部如何通訊,這個不是咱們所關心的內容,由於若是要深刻進去估計沒完沒了,有興趣的朋友能夠參考源碼進入SocketListener查看:this

創建鏈接 spa

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 <  0break;

                 //  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, eventevent.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 進行判斷,而後再下發執行命令。 

 

粗糙圖

最後附一張比較粗糙的結構圖,時間較急,沒仔細畫好

 

相關文章
相關標籤/搜索