1. 程式人生 > >【輸出文件】 Android MountService 原始碼分析

【輸出文件】 Android MountService 原始碼分析

Android 儲存裝置管理框架

androidVOLD程序啟動原始碼分析一文中介紹了儲存裝置的管控中心Vold程序,Vold屬於native後臺程序,通過netlink方式接收kerneluevent訊息,並通過socket方式將uevent訊息傳送給MountService,同時實時接收MountService的命令訊息,MountServiceVoldKernel三者的關係如下圖所示:

androidVOLD程序啟動原始碼分析一文中介紹了NetlinkManager模組在啟動過程中,建立了一個socket監聽執行緒,用於監聽kernel傳送過來的uevent訊息

CommandListener模組在啟動時同樣建立了一個socket監聽執行緒,不同的是該執行緒用於監聽MountServcie的連線接收MountServiceVold傳送的命令訊息MountService要接收來自kerneluevent訊息,必定也需要建立一個socket監聽執行緒,在接下來將對該socket監聽執行緒進行詳細講解。

Android MountService框架設計

MountService作為AndroidJava服務之一,在SystemServer程序啟動的第二階段建立並註冊到ServiceManager中,同時長駐於SystemServer程序中,

MountService建立及註冊過程如下:

[java] view plaincopy

 

MountService mountService = null; 
if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) { 
    try { 
        /*
         * NotificationManagerService is dependant on MountService,
         * so we must start MountService first.
         */ 
        Slog.i(TAG, "Mount Service"); 
        mountService = new MountService(context); 
        //註冊到ServiceManager中 
        ServiceManager.addService("mount", mountService); 
    } catch (Throwable e) { 
        reportWtf("starting Mount Service", e); 
    } 
} 

MountService各個類關係圖:

構造MountService物件例項:

[java] view plaincopy

 

  1. public MountService(Context context) { 
        mContext = context;   
        //從xml中讀取儲存裝置列表 
        readStorageList(); 
      
        if (mPrimaryVolume != null) { 
            mExternalStoragePath = mPrimaryVolume.getPath(); 
            mEmulateExternalStorage = mPrimaryVolume.isEmulated(); 
            if (mEmulateExternalStorage) { 
                Slog.d(TAG, "using emulated external storage"); 
                mVolumeStates.put(mExternalStoragePath, Environment.MEDIA_MOUNTED); 
            } 
        } 
          
        //add mount state for inernal storage in NAND 
        if (Environment.getSecondStorageType() == Environment.SECOND_STORAGE_TYPE_NAND) { 
            mVolumeStates.put(Environment.getSecondStorageDirectory().getPath(), Environment.MEDIA_MOUNTED); 
        } 
        // 查詢PackageManagerService服務 
        mPms = (PackageManagerService) ServiceManager.getService("package"); 
        IntentFilter filter = new IntentFilter(); 
        filter.addAction(Intent.ACTION_BOOT_COMPLETED); 
        // don't bother monitoring USB if mass storage is not supported on our primary volume 
        if (mPrimaryVolume != null && mPrimaryVolume.allowMassStorage()) { 
            filter.addAction(UsbManager.ACTION_USB_STATE); 
        } 
        //註冊開機完成及USB狀態變化廣播接收器 
        mContext.registerReceiver(mBroadcastReceiver, filter, null, null); 
        //建立並啟動一個帶訊息迴圈的MountService工作執行緒 
        mHandlerThread = new HandlerThread("MountService"); 
        mHandlerThread.start(); 
        //為MountService工作執行緒建立一個Handler 
        mHandler = new MountServiceHandler(mHandlerThread.getLooper()); 
      
        //為MountService工作執行緒建立一個ObbActionHandler 
        mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper()); 
      
        /*
         * 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", MAX_CONTAINERS * 2, VOLD_TAG, 25); 
        //建立並啟動一個socket連線監聽執行緒 
        Thread thread = new Thread(mConnector, VOLD_TAG); 
        thread.start(); 
      
        // Add ourself to the Watchdog monitors if enabled. 
        if (WATCHDOG_ENABLE) { 
            Watchdog.getInstance().addMonitor(this); 
        } 
    } 

     

在開始構造MountService前,首先讀取frameworks/base/core/res/res/xml/storage_list.xml檔案,該檔案以XML方式儲存了所有儲存裝置的引數,檔案內容如下所示:

[html] view plaincopy

 

<StorageList xmlns:android="http://schemas.android.com/apk/res/android"> 
    <!-- removable is not set in nosdcard product --> 
    <storage android:mountPoint="/mnt/sdcard" 
                 android:storageDescription="@string/storage_usb" 
                 android:primary="true" /> 
</StorageList> 

該檔案的讀取這裡不在介紹,讀者自行研究,使用XML解析器讀取該XML的檔案內容,根據讀取到的儲存裝置引數來構造StorageVolume物件,並將構造的所有StorageVolume物件存放到列表mVolumes。通過原始碼清晰地知道,在構造MountService時,註冊了一個廣播接收器,用於接收開機完成廣播及USB狀態廣播,當開機完成時自動掛載儲存裝置,在大容量裝置儲存有效情況下,當USB狀態變化也自動地掛載儲存裝置。建立了兩個工作執行緒,MountService執行緒用於訊息迴圈處理,為什麼要開啟一個非同步訊息處理執行緒呢?我們知道大量的Java Service駐留在SystemServer程序中,如果所有的服務訊息都發送到SystemServer的主執行緒中處理的話,主執行緒的負荷很重,訊息不能及時得到處理,因此需為每一個Service開啟一個訊息處理執行緒,專門處理本Service的訊息。如下圖所示:

 

MountService服務的執行緒模型

從上圖可以清晰地看出SystemServer主執行緒啟動MountService服務,該服務啟動時會建立一個MountService帶有訊息迴圈的工作執行緒,用於處理MountServiceHandleObbActionHandler分發過來的訊息;同時建立一個用於連線Vold服務端socketVoldConnector執行緒,該執行緒在進入閉環執行前會建立一個帶有訊息迴圈的VoldConnector.CallbackHandler執行緒,用於處理native層的Vold程序傳送過來的uevent事件訊息;然後向服務端Vold傳送連線請求,得到socket連線後,從該socket中迴圈讀取資料以接收來之服務端Volduevent訊息,當讀取的資料長度為0時,向服務端重新發起連線,如此迴圈,保證客戶端MountService與服務端Vold一直保持正常連線。當成功連線到服務端Vold時,VoldConnector執行緒會建立一個MountService#onDaemonConnected執行緒,用於處理本次連線請求響應。

1.MountService執行緒建立

[java] view plaincopy

 

  1. mHandlerThread = new HandlerThread("MountService"); 
    mHandlerThread.start(); 
    mHandler = new MountServiceHandler(mHandlerThread.getLooper()); 
      
    // Add OBB Action Handler to MountService thread. 
    mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper()); 

     

MountServiceHandler訊息處理:

[java] view plaincopy



public void handleMessage(Message msg) { 
    switch (msg.what) { 
    case H_UNMOUNT_PM_UPDATE: { 
        UnmountCallBack ucb = (UnmountCallBack) msg.obj; 
        mForceUnmounts.add(ucb); 
        // Register only if needed. 
        if (!mUpdatingStatus) { 
        if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager"); 
        mUpdatingStatus = true; 
        mPms.updateExternalMediaStatus(false, true); 
        } 
        break; 
    } 
    case H_UNMOUNT_PM_DONE: { 
        mUpdatingStatus = false; 
        int size = mForceUnmounts.size(); 
        int sizeArr[] = new int[size]; 
        int sizeArrN = 0; 
        // Kill processes holding references first 
        ActivityManagerService ams = (ActivityManagerService) 
        ServiceManager.getService("activity"); 
        for (int i = 0; i < size; i++) { 
        UnmountCallBack ucb = mForceUnmounts.get(i); 
        String path = ucb.path; 
        boolean done = false; 
        if (!ucb.force) { 
            done = true; 
        } else { 
            int pids[] = getStorageUsers(path); 
            if (pids == null || pids.length == 0) { 
            done = true; 
            } else { 
            // Eliminate system process here? 
            ams.killPids(pids, "unmount media", true); 
            // Confirm if file references have been freed. 
            pids = getStorageUsers(path); 
            if (pids == null || pids.length == 0) { 
                done = true; 
            } 
            } 
        } 
        if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) { 
            // Retry again 
            Slog.i(TAG, "Retrying to kill storage users again"); 
            mHandler.sendMessageDelayed(mHandler.obtainMessage(H_UNMOUNT_PM_DONE,ucb.retries++),RETRY_UNMOUNT_DELAY); 
        } else { 
            if (ucb.retries >= MAX_UNMOUNT_RETRIES) { 
            Slog.i(TAG, "Failed to unmount media inspite of " + 
                MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now"); 
            } 
            sizeArr[sizeArrN++] = i; 
            mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,ucb)); 
        } 
        } 
        // Remove already processed elements from list. 
        for (int i = (sizeArrN-1); i >= 0; i--) { 
        mForceUnmounts.remove(sizeArr[i]); 
        } 
        break; 
    } 
    case H_UNMOUNT_MS: { 
        UnmountCallBack ucb = (UnmountCallBack) msg.obj; 
        ucb.handleFinished(); 
        break; 
    } 
    } 
} 

MountServiceHandler分別對H_UNMOUNT_PM_UPDATE,H_UNMOUNT_PM_DONE,H_UNMOUNT_MS

[java] view plaincopy

 


  1.  

MountService命令下發流程

Vold作為儲存裝置的管控中心,需要接收來自上層MountService的操作命令,MountService駐留在SystemServer程序中,和Vold作為兩個不同的程序,它們之間的通訊方式採用的是socket通訊,在androidVOLD程序啟動原始碼分析一文中介紹了CommandListener模組啟動了一個socket監聽執行緒,用於專門接收來之上層MountService的連線請求[p1] 。而在MountService這端,同樣啟動了VoldConnector socket連線執行緒,用於迴圈連線服務端,保證連線不被中斷,當成功連線Vold時,迴圈從服務端讀取資料。MountService按照指定格式向Vold傳送命令,由於傳送的命令比較多,這裡不做一一接收,只對其中的mount命令的傳送流程進行介紹:

從以上的時序圖可以看出,MountService對命令的傳送首先是呼叫makeCommand來組合成指定格式的命令[p2] ,然後直接寫入到Vold socket即可,整個命令傳送流程比較簡單。

 

[java] view plaincopy

 

  1. public int mountVolume(String path) { 
            //許可權檢驗 
        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 
        waitForReady(); 
      
        return doMountVolume(path); 
    } 

     

呼叫doMountVolume函式來掛載儲存裝置:

 

[java] view plaincopy

 

  1. private int doMountVolume(String path) { 
        int rc = StorageResultCode.OperationSucceeded; 
        try { 
            //命令交給NativeDaemonConnector去傳送 
            mConnector.execute("volume", "mount", path); 
        } catch (NativeDaemonConnectorException e) { 
            //捕獲命令傳送的異常,根據異常碼來決定傳送失敗的原因 
            String action = null; 
            int code = e.getCode(); 
            if (code == VoldResponseCode.OpFailedNoMedia) { 
            /*
             * Attempt to mount but no media inserted
             */ 
            rc = StorageResultCode.OperationFailedNoMedia; 
            } else if (code == VoldResponseCode.OpFailedMediaBlank) { 
            if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs"); 
            /*
             * Media is blank or does not contain a supported filesystem
             */ 
            updatePublicVolumeState(path, Environment.MEDIA_NOFS); 
            action = Intent.ACTION_MEDIA_NOFS; 
            rc = StorageResultCode.OperationFailedMediaBlank; 
            } else if (code == VoldResponseCode.OpFailedMediaCorrupt) { 
            if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt"); 
            /*
             * Volume consistency check failed
             */ 
            updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE); 
            action = Intent.ACTION_MEDIA_UNMOUNTABLE; 
            rc = StorageResultCode.OperationFailedMediaCorrupt; 
            } else { 
            rc = StorageResultCode.OperationFailedInternalError; 
            } 
            /*
             * Send broadcast intent (if required for the failure)
             */ 
            if (action != null) { 
            sendStorageIntent(action, path); 
            } 
        } 
        return rc; 
    } 

     

NativeDaemonConnector命令傳送:

 

[java] view plaincopy

 

  1. public NativeDaemonEvent execute(String cmd, Object... args) 
        throws NativeDaemonConnectorException { 
            //使用executeForList函式來發送命令和命令引數,並返回一組NativeDaemonEvent事件 
        final NativeDaemonEvent[] events = executeForList(cmd, args); 
        if (events.length != 1) { 
            throw new NativeDaemonConnectorException("Expected exactly one response, but received " + events.length); 
        } 
        return events[0]; 
    } 

     

呼叫executeForList來發送命令和命令引數,並在這裡設定超時時間:

 

[java] view plaincopy

 

  1. public NativeDaemonEvent[] executeForList(String cmd, Object... args) 
        throws NativeDaemonConnectorException { 
        //設定超時時間:DEFAULT_TIMEOUT = 1 * 60 * 1000 
        return execute(DEFAULT_TIMEOUT, cmd, args); 
    } 

     

真正命令傳送:

[java] view plaincopy

 

  1. public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args) 
        throws NativeDaemonConnectorException { 
        final ArrayList<NativeDaemonEvent> events = Lists.newArrayList(); 
        final int sequenceNumber = mSequenceNumber.incrementAndGet(); 
        final StringBuilder cmdBuilder = new StringBuilder(Integer.toString(sequenceNumber)).append(' '); 
        //傳送起始時間 
        final long startTime = SystemClock.elapsedRealtime(); 
        //命令組合 
        makeCommand(cmdBuilder, cmd, args); 
        final String logCmd = cmdBuilder.toString(); /* includes cmdNum, cmd, args */ 
        log("SND -> {" + logCmd + "}"); //SND -> {8 volume mount /storage/sdcard1} 
        cmdBuilder.append('\0'); 
        final String sentCmd = cmdBuilder.toString(); /* logCmd + \0 */ 
        synchronized (mDaemonLock) { 
            if (mOutputStream == null) { 
            throw new NativeDaemonConnectorException("missing output stream"); 
            } else { 
            try { 
                //向socket中寫入命令 
                mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8)); 
            } catch (IOException e) { 
                throw new NativeDaemonConnectorException("problem sending command", e); 
            } 
            } 
        } 
        NativeDaemonEvent event = null; 
        do { 
            event = mResponseQueue.remove(sequenceNumber, timeout, sentCmd); 
            if (event == null) { 
            loge("timed-out waiting for response to " + logCmd); 
            throw new NativeDaemonFailureException(logCmd, event); 
            } 
            log("RMV <- {" + event + "}"); 
            events.add(event); 
        } while (event.isClassContinue()); 
            //傳送結束時間 
        final long endTime = SystemClock.elapsedRealtime(); 
        if (endTime - startTime > WARN_EXECUTE_DELAY_MS) { 
            loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)"); 
        } 
        if (event.isClassClientError()) { 
            throw new NativeDaemonArgumentException(logCmd, event); 
        } 
        if (event.isClassServerError()) { 
            throw new NativeDaemonFailureException(logCmd, event); 
        } 
        return events.toArray(new NativeDaemonEvent[events.size()]); 
    } 
    

     

MountService訊息接收流程[p3] 

MountService需要接收兩種型別的訊息:

1當外部儲存裝置發生熱插拔時,kernel將通過netlink方式通知VoldVold程序經過一系列處理後最終還是要叫uevent事件訊息傳送給MountService,Vold傳送uevent的過程已經在androidVOLD程序啟動原始碼分析一文中詳細介紹了;

2)當MountServiceVold傳送命令後,將接收到Vold的響應訊息;

無論是何種型別的訊息,MountService都是通過VoldConnector執行緒來迴圈接收Vold的請求,整個訊息接收流程如下:

1socket連線

[java] view plaincopy

 

  1. public void run() { 
        //建立並啟動VoldConnector.CallbackHandler執行緒,用於處理native層的Vold程序傳送過來的uevent事件訊息 
        HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler"); 
        thread.start(); 
        //為VoldConnector.CallbackHandler執行緒建立一個Handler,用於向該執行緒分發訊息 
        mCallbackHandler = new Handler(thread.getLooper(), this); 
            //進入閉環socket連線模式 
        while (true) { 
            try { 
                listenToSocket(); 
            } catch (Exception e) { 
                loge("Error in NativeDaemonConnector: " + e); 
                SystemClock.sleep(5000); 
            } 
        } 
    } 

     

連線服務端Socket,並讀取資料:

[java] view plaincopy

 

  1. private void listenToSocket() throws IOException { 
        LocalSocket socket = null; 
        try { 
            //建立Vold socket 
            socket = new LocalSocket(); 
            LocalSocketAddress address = new LocalSocketAddress(mSocket,LocalSocketAddress.Namespace.RESERVED); 
                    //向服務端發起連線請求 
            socket.connect(address); 
                    //從連線的socket中得到輸入輸出流 
            InputStream inputStream = socket.getInputStream(); 
            synchronized (mDaemonLock) { 
                mOutputStream = socket.getOutputStream(); 
            } 
                    //對本次連線請求做一些回撥處理 
            mCallbacks.onDaemonConnected();  
                    //定義buffer 
            byte[] buffer = new byte[BUFFER_SIZE]; 
            int start = 0; 
                    //進入閉環資料讀取模式 
            while (true) { 
                int count = inputStream.read(buffer, start, BUFFER_SIZE - start); 
                //當讀取的資料長度小於0時,表示連線已斷開,跳出迴圈,重新向服務端發起新的連線請求 
                if (count < 0) { 
                    loge("got " + count + " reading with start = " + start); 
                    break; 
                } 
                // Add our starting point to the count and reset the start. 
                count += start; 
                start = 0; 
                            //解析讀取到的資料,得到NativeDaemonEvent 
                for (int i = 0; i < count; i++) { 
                    if (buffer[i] == 0) { 
                        final String rawEvent = new String(buffer, start, i - start, Charsets.UTF_8); 
                        //RCV <- {632 Volume sdcard /storage/sdcard1 bad removal (179:1)} 
                        log("RCV <- {" + rawEvent + "}"); 
      
                        try { 
                            final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(rawEvent); 
                            //如果命令碼code >= 600 && code < 700 
                            if (event.isClassUnsolicited()) { 
                                //將讀取到的事件傳送到VoldConnector.CallbackHandler執行緒中處理 
                                mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage( 
                                        event.getCode(), event.getRawEvent())); 
                            //否則將改事件新增到響應佇列中 
                            } else {  
                                mResponseQueue.add(event.getCmdNumber(), event); 
                            } 
                        } catch (IllegalArgumentException e) { 
                            log("Problem parsing message: " + rawEvent + " - " + e); 
                        } 
                        start = i + 1; 
                    } 
                } 
                if (start == 0) { 
                    final String rawEvent = new String(buffer, start, count, Charsets.UTF_8); 
                    log("RCV incomplete <- {" + rawEvent + "}"); 
                } 
                // 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) { 
            loge("Communications error: " + ex); 
            throw ex; 
        } finally { 
            synchronized (mDaemonLock) { 
                if (mOutputStream != null) { 
                    try { 
                        loge("closing stream for " + mSocket); 
                        mOutputStream.close(); 
                    } catch (IOException e) { 
                        loge("Failed closing output stream: " + e); 
                    } 
                    mOutputStream = null; 
                } 
            } 
            try { 
                if (socket != null) { 
                    socket.close(); 
                } 
            } catch (IOException ex) { 
                loge("Failed closing socket: " + ex); 
            } 
        } 
    } 

     

2)連線成功回撥處理

[java] view plaincopy

 

  1. public void onDaemonConnected() { 
        //建立一個工作執行緒 
        new Thread("MountService#onDaemonConnected") { 
            @Override 
            public void run() { 
                /**
                 * Determine media state and UMS detection status
                 */ 
                try { 
                    //向vold查詢所有的儲存裝置 
                    final String[] vols = NativeDaemonEvent.filterMessageList( 
                            mConnector.executeForList("volume", "list"), 
                            VoldResponseCode.VolumeListResult); 
                        //判斷儲存裝置狀態 
                    for (String volstr : vols) { 
                        String[] tok = volstr.split(" "); 
                        // FMT: <label> <mountpoint> <state> 
                        String path = tok[1]; 
                        String state = Environment.MEDIA_REMOVED; 
      
                        int st = Integer.parseInt(tok[2]); 
                        if (st == VolumeState.NoMedia) { 
                            state = Environment.MEDIA_REMOVED; 
                        } else if (st == VolumeState.Idle) { 
                            state = Environment.MEDIA_UNMOUNTED; 
                        } else if (st == VolumeState.Mounted) { 
                            state = Environment.MEDIA_MOUNTED; 
                            Slog.i(TAG, "Media already mounted on daemon connection"); 
                        } else if (st == VolumeState.Shared) { 
                            state = Environment.MEDIA_SHARED; 
                            Slog.i(TAG, "Media shared on daemon connection"); 
                        } else { 
                            throw new Exception(String.format("Unexpected state %d", st)); 
                        } 
      
                        if (state != null) { 
                            if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state); 
                            //更新Volume狀態 
                            updatePublicVolumeState(path, state); 
                        } 
                    } 
                } catch (Exception e) { 
                    Slog.e(TAG, "Error processing initial volume state", e); 
                    updatePublicVolumeState(mExternalStoragePath, Environment.MEDIA_REMOVED); 
                } 
      
                /*
                 * Now that we've done our initialization, release
                 * the hounds!
                 */ 
                mConnectedSignal.countDown(); 
                mConnectedSignal = null; 
      
                // 使用PackageManagerService掃描外邊儲存裝置上的APK資訊 
                mPms.scanAvailableAsecs(); 
      
                // Notify people waiting for ASECs to be scanned that it's done. 
                mAsecsScanned.countDown(); 
                mAsecsScanned = null; 
            } 
        }.start(); 
    } 

     

儲存裝置狀態更新:

[java] view plaincopy

 

  1. private boolean updatePublicVolumeState(String path, String state) { 
        String oldState; 
        synchronized(mVolumeStates) { 
            oldState = mVolumeStates.put(path, state); 
        } 
        if (state.equals(oldState)) { 
            Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",state, state, path)); 
            return false; 
        } 
        Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")"); 
        if (path.equals(mExternalStoragePath)) { 
            // Update state on PackageManager, but only of real events 
            if (!mEmulateExternalStorage) { 
                if (Environment.MEDIA_UNMOUNTED.equals(state)) { 
                    mPms.updateExternalMediaStatus(false, false); 
      
                    /*
                     * Some OBBs might have been unmounted when this volume was
                     * unmounted, so send a message to the handler to let it know to
                     * remove those from the list of mounted OBBS.
                     */ 
                    mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE, path)); 
                } else if (Environment.MEDIA_MOUNTED.equals(state)) { 
                    mPms.updateExternalMediaStatus(true, false); 
                } 
            } 
        } 
        synchronized (mListeners) { 
            for (int i = mListeners.size() -1; i >= 0; i--) { 
                MountServiceBinderListener bl = mListeners.get(i); 
                try { 
                    //呼叫已註冊的MountServiceBinderListener來通知儲存裝置狀態改變 
                    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); 
                } 
            } 
        } 
        return true; 
    } 

     

 

MountServiceBinderListener的註冊過程:

[java] view plaincopy

 

  1. public void registerListener(IMountServiceListener listener) { 
        synchronized (mListeners) { 
            MountServiceBinderListener bl = new MountServiceBinderListener(listener); 
            try { 
                listener.asBinder().linkToDeath(bl, 0); 
                mListeners.add(bl); 
            } catch (RemoteException rex) { 
                Slog.e(TAG, "Failed to link to listener death"); 
            } 
        } 
    } 

     

使用StorageManager的內部類MountServiceBinderListener物件來構造MountService的內部類MountServiceBinderListener物件,並新增到MountService的成員變數mListeners列表中。StorageManager的內部類MountServiceBinderListener定義如下:

[java] view plaincopy

 

private c