天天看點

深入了解Android MTP之UsbService啟動分析

閑話

公司接了項目,開發一個線上更新功能,其中我需要實作手機端與PC端的通信。公司選擇使用MTP來實作這個需求,是以我分析了大量的關于MTP的代碼,從frameworks層到app,再到JNI層。鑒于網上關于這樣的文章太少,而我開發的過程也比較長,是以我決定把framework, app , JNI層的分析都寫下來,希望能幫助開發類似功能的小夥伴。

鄧凡平老師寫的深入了解Android系列書籍,有的已經不出版了,但是他在部落格中把文章的所有内容都釋出出來,他說知識需要傳遞。這一點,我深感佩服。

服務開啟

UsbService是一個系統服務,它在system_server程序中建立并注冊的。

private static final String USB_SERVICE_CLASS =
        "com.android.server.usb.UsbService$Lifecycle";
            
private void startOtherServices() {
    // ...
    
    mSystemServiceManager.startService(USB_SERVICE_CLASS);
    
    // ...   
}
           

SystemServiceManager通過反射建立UsbService$Lifecycle對象(Lifecycle是UsbService的一個内部類),然後加入到List集合中,最後調用Lifcycle對象的onStart方法。

SystemServiceManager儲存了各種服務,并且會把系統啟動的各個階段告訴服務,我們可以看看UsbService$Lifecycle的各種生命周期回調

public class UsbService extends IUsbManager.Stub {

    public static class Lifecycle extends SystemService {
    
        // 服務建立階段
        @Override
        public void onStart() {
            
            mUsbService = new UsbService(getContext());
            publishBinderService(Context.USB_SERVICE, mUsbService);
        }
        
        // 響應系統啟動階段
        @Override
        public void onBootPhase(int phase) {
            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
				// 系統就緒階段
                mUsbService.systemReady();
            } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
            	// 系統啟動完畢
                mUsbService.bootCompleted();
            }
        }        
    }
}
           

可以看到,一個服務會經過建立階段,系統就緒階段,系統啟動完畢階段。接下來,分為三部分來分析UsbService的啟動過程。

服務建立階段

在服務建立階段,首先建立了UsbService一個對象,由于UsbService是一個Binder對象,然後就把這個服務釋出到ServiceManager。釋出這個服務後,用戶端就可以通路這個服務。

現在來看下UsbService的構造函數

public UsbService(Context context) {
        // ...
        
        if (new File("/sys/class/android_usb").exists()) {
            mDeviceManager = new UsbDeviceManager(context, mAlsaManager, mSettingsManager);
        }

        // ...
    }
           

在構造函數中,與MTP相關的主要代碼就是建立UsbDeviceManager對象。

MTP模式下,Android裝置是作為Device端,UsbDeviceManager就是用來處理Device端的事務。

現在來看下UsbDeviceManager的構造函數做了什麼

public UsbDeviceManager(Context context, UsbAlsaManager alsaManager,
            UsbSettingsManager settingsManager) {
        // ...
        
        // 我的項目不支援MTP的Hal層
        if (halNotPresent) {
            // 1. 初始化mHandler
            mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext, this,
                    alsaManager, settingsManager);
        } else {
            // ...
        }


        // 2. 注冊各種廣播
        //... 這裡其實注冊了很多個廣播接收器(隻不過和省略代碼了)
        BroadcastReceiver chargingReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
                boolean usbCharging = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
                mHandler.sendMessage(MSG_UPDATE_CHARGING_STATE, usbCharging);
            }
        };

        mContext.registerReceiver(chargingReceiver,
                new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        
        
        // 3. 監聽USB狀态改變
        mUEventObserver = new UsbUEventObserver();
        mUEventObserver.startObserving(USB_STATE_MATCH);
        mUEventObserver.startObserving(USB_STATE_MATCH_SEC);
        mUEventObserver.startObserving(ACCESSORY_START_MATCH);
    }
           

UsbDeviceManager的構造函數做了三件事。

第一件事,初始化mHandler對象。由于我的項目不支援MTP的Hal層,是以mHandler的初始化使用的是UsbHandlerLegacy對象。

第二事,注冊了各種廣播接收器,例如端口變化,語言變化,等等。這裡我把關于充電的廣播接收器代碼展示出來了。當我們把手機通過USB線連接配接到電腦端的時候,手機會充電,并且手機上會出現一個關于USB充電的通知。打開這個關于USB的通知,我們就可以切換USB的功能,例如MTP, PTP,等等。

第三件事,通過Linux Uevent機制監聽USB狀态變換。當手機通過USB線連接配接電腦時,USB狀态會從DISCONNECTED變為CONNECTED,再變為CONFIGURED。當狀态改變會處理usb狀态更新操作,這個過程在後面會分析到。

現在來看看UsbHandlerLegacy對象的建立。

UsbHandlerLegacy(Looper looper, Context context, UsbDeviceManager deviceManager,
                UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
            // 父類構造函數初始化了一些參數,大家可以根據需要自己分析
            super(looper, context, deviceManager, alsaManager, settingsManager);
            try {
                // 1. 讀取oem覆寫配置
                readOemUsbOverrideConfig(context);
                
                // 2. 讀取各種屬性的值
                // 2.1 正常模式,讀取的是persist.sys.usb.config屬性的值
                mCurrentOemFunctions = getSystemProperty(getPersistProp(false),
                        UsbManager.USB_FUNCTION_NONE);
                        
                // ro.bootmode屬性的值為normal或unknown,就表示正常啟動
                if (isNormalBoot()) {
                    // 2.2 讀取sys.usb.config屬性的值,這個屬性表示目前設定的usb功能
                    mCurrentFunctionsStr = getSystemProperty(USB_CONFIG_PROPERTY,
                            UsbManager.USB_FUNCTION_NONE);
                    // 2.3 比較sys.usb.config屬性與sys.usb.state屬性的值
                    // sys.usb.state屬性表示usb的實際功能
                    // 如果兩個屬性相等,表示usb設定的功能都起效了
                    mCurrentFunctionsApplied = mCurrentFunctionsStr.equals(
                            getSystemProperty(USB_STATE_PROPERTY, UsbManager.USB_FUNCTION_NONE));
                } else {

                }
                
                // mCurrentFunctions代表目前要設定的usb功能,初始值為0
                mCurrentFunctions = UsbManager.FUNCTION_NONE;
                mCurrentUsbFunctionsReceived = true;
                
                // 3. 讀取一次usb狀态,然後做一次更新操作
                String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
                updateState(state);
            } catch (Exception e) {
                Slog.e(TAG, "Error initializing UsbHandler", e);
            }
        }
           

UsbHandlerLegacy的構造函數大緻分為三步。首先看第一步,讀取oem廠商的關于usb功能的覆寫配置。

private void readOemUsbOverrideConfig(Context context) {
            // 數組每一項的格式為[bootmode]:[original USB mode]:[USB mode used]
            String[] configList = context.getResources().getStringArray(
                    com.android.internal.R.array.config_oemUsbModeOverride);

            if (configList != null) {
                for (String config : configList) {
                    String[] items = config.split(":");
                    if (items.length == 3 || items.length == 4) {
                        if (mOemModeMap == null) {
                            mOemModeMap = new HashMap<>();
                        }
                        HashMap<String, Pair<String, String>> overrideMap =
                                mOemModeMap.get(items[0]);
                        if (overrideMap == null) {
                            overrideMap = new HashMap<>();
                            mOemModeMap.put(items[0], overrideMap);
                        }

                        // Favoring the first combination if duplicate exists
                        if (!overrideMap.containsKey(items[1])) {
                            if (items.length == 3) {
                                overrideMap.put(items[1], new Pair<>(items[2], ""));
                            } else {
                                overrideMap.put(items[1], new Pair<>(items[2], items[3]));
                            }
                        }
                    }
                }
            }
        }
           

讀取的是

config_oemUsbModeOverride

數組,然後儲存到mOemModeMap中。數組每一項的格式為

[bootmode]:[original USB mode]:[USB mode used]

,儲存的格式可以大緻描述為

HashMap<bootmode, HashMap<original_usb_mode, Pair<usb_mode_used, "">

。我的項目中,這個數組為空。

然後第二步,讀取了各種屬性值(隻考慮正常啟動模式),如下。

  1. mCurrentOemFunctions的值是

    persist.sys.usb.config

    屬性的值。按照源碼注釋,這個屬性值存儲了adb的開啟狀态(如果開啟了adb,那麼這個值會包含adb字元串)。另外,源碼注釋說這個屬性也可以營運商定制的一些功能,但是隻用于測試目的。
  2. mCurrentFunctionsStr的值是

    sys.usb.config

    屬性值。這個屬性表示目前設定的usb功能的值。在日常工作中,我們可以通過adb shell指令設定這個屬性值來切換usb功能,例如

    adb shell setprop sys.usb.config mtp,adb

    可以切換到mtp功能。
  3. 如果通過

    sys.usb.config

    屬性切換功能成功,那麼

    sys.usb.state

    屬性值就與

    sys.usb.config

    屬性值一樣。也就是說

    sys.usb.state

    代表usb的實際功能的值。是以,可以通過比較這兩個屬性值來判斷usb所有功能是否切換成功,如果成功了,mCurrentFunctionsApplied的值為1,否則為0。

第三步,讀取了目前usb狀态,并且做了一次更新操作。更新操作會發送相關通知,以及發送廣播,但是現在處理服務建立階段,這個操作都無法執行,是以這裡不做分析。但是當處理系統就緒階段或系統啟動完畢階段,就可以做相應的操作,在後面的分析中可以看到。

系統就緒階段

根據前面的代碼,在系統就緒階段,會調用UsbService的systemRead()方法,然後轉到UsbDeviceManager的systemRead()方法

public void systemReady() {
        // 注冊一個關于螢幕狀态的回調,有兩個方法
        LocalServices.getService(ActivityTaskManagerInternal.class).registerScreenObserver(this);

        mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
    }
           

首先注冊了一個關于螢幕的回調,這個回調用于處理在安全鎖屏下,設定usb的功能。但是這個功能好像處于開發階段,隻能通過

adb shell

指令操作,通過輸入

adb shell svc usb

可以檢視使用幫助。

接下來,發送了一個消息

MSG_SYSTEM_READY

,我們來看下這個消息是如何處理的

case MSG_SYSTEM_READY:
            // 擷取到notification服務接口
            mNotificationManager = (NotificationManager)
                    mContext.getSystemService(Context.NOTIFICATION_SERVICE);

            // 向adb service注冊一個回調,用于狀态adb相關的狀态
            LocalServices.getService(
                    AdbManagerInternal.class).registerTransport(new AdbTransport(this));

            // Ensure that the notification channels are set up
            if (isTv()) {
                // ...
            }
            // 設定系統就緒的标志位
            mSystemReady = true;
            // 此時系統還沒有啟動完成,這裡沒有做任何事
            // 這應該是曆史原因造成的代碼備援
            finishBoot();
            break;
           

可以看到,在系統就緒階段才擷取到了通知服務的接口,這也從側面證明了在UsbService建立階段,是無法發送通知的。然而我卻有點疑惑,通知服務和UsbService同程序,并且通知服務也有内部接口可供系統服務調用,為何這裡還要通過NotificationManager發送廣播?難道隻是為寫代碼友善,但是這樣一來,不就進行了一次不必要的Binder通信(可能這是我的妄言!)?

擷取通知服務接口後,就向adb服務注冊了一個回調,可以通過這個回調,可以接收到關于adb開戶/關閉的消息。

系統啟動完畢階段

現在來看下最後一個階段,系統啟動完畢階段。根據前面的代碼,會調用UsbService的bootcompleted()方法,然後調用UsbDeviceManager的bootcompleted()方法

public void bootCompleted() {
        mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
    }
           

隻是發送了一條消息,看下消息如何處理的

case MSG_BOOT_COMPLETED:
			// 設定系統啟動完成的标志
            mBootCompleted = true;
            finishBoot();
            break;
           

很簡單,設定了一個啟動标志,然後就調用finishBoot()方法完成最後的任務

protected void finishBoot() {
            if (mBootCompleted && mCurrentUsbFunctionsReceived && mSystemReady) {
                // mPendingBootBroadcast是在服務建立階段設定的
                if (mPendingBootBroadcast) {
                    // 1. 發送/更新usb狀态改變的廣播
                    updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
                    mPendingBootBroadcast = false;
                }
                if (!mScreenLocked
                        && mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
                    // 這個功能還是處于調試階段,不分析
                    setScreenUnlockedFunctions();
                } else {
                    // 2. 設定USB功能為NONE
                    setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
                }
                // 關于Accessory功能
                if (mCurrentAccessory != null) {
                    mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
                }
				// 3. 如果手機已經連接配接電腦就發送usb通知,通過這個通知,可以選擇usb模式
                updateUsbNotification(false);
				// 4. 如果adb已經開啟,并且手機已經連接配接電腦,就發送adb通知
                updateAdbNotification(false);
                // 關于MIDI功能
                updateUsbFunctions();
            }
        }
           

如果現在手機沒有通過USB線連接配接電腦,那麼第一步的發送USB狀态廣播,第三步的USB通知,第四步adb通知,都無法執行。唯一能執行的就是第二步,設定USB功能為NONE。

OK,現在終于到最關鍵的一步,設定USB功能,它調用的是

setEnabledFunctions()

方法。這個方法本身是一想抽象方法,在我的項目中,實作類為

UsbHandlerLegacy

protected void setEnabledFunctions(long usbFunctions, boolean forceRestart) {
            // 判斷資料是否解鎖,隻有MTP和PTP的資料是解鎖的
            boolean usbDataUnlocked = isUsbDataTransferActive(usbFunctions);
            
            // 處理資料解鎖狀态改變的情況
            if (usbDataUnlocked != mUsbDataUnlocked) {
                // 更新資料解鎖狀态
                mUsbDataUnlocked = usbDataUnlocked;
                // 更新usb通知
                updateUsbNotification(false);
                // forceRestart設定為true,表示需要強制重新開機usb功能
                forceRestart = true;
            }
            
            // 在設定新usb功能前,先儲存舊的狀态,以免設定新功能失敗,還可以恢複
            final long oldFunctions = mCurrentFunctions;
            final boolean oldFunctionsApplied = mCurrentFunctionsApplied;
            
            // 嘗試設定usb新功能
            if (trySetEnabledFunctions(usbFunctions, forceRestart)) {
                return;
            }

            // 如果到這裡,就表示新功能設定失敗,那麼就回退之前的狀态
            if (oldFunctionsApplied && oldFunctions != usbFunctions) {
                Slog.e(TAG, "Failsafe 1: Restoring previous USB functions.");
                if (trySetEnabledFunctions(oldFunctions, false)) {
                    return;
                }
            }

            // 如果回退還是失敗了,那麼就設定usb功能為NONE
            if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) {
                return;
            }

            // 如果設定NONE還是失敗了,那麼再試一次設定NONE
            if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) {
                return;
            }
            
            // 如果走到這裡,就表示異常了。
            Slog.e(TAG, "Unable to set any USB functions!");
        }
           

首先判斷要設定的新的USB功能的資料是否是解鎖狀态,隻有MTP和PTP模式的資料是解鎖狀态,這是為何你能在設定MTP或PTP模式後,在PC端能看到手機中的檔案,然而這個檔案隻是手機記憶體中檔案的映射,并不是檔案本身。

然後處理資料解鎖狀态改變的情況,如果是,那麼會更新狀态,更新usb廣播,然後最重要的是設定

forceRestart

變量的值為

true

,這個變量代表要強制重新開機usb功能。

最後,設定新usb功能。如果失敗了,就回退。現在來看下

trySetEnabledFunctions()

方法如何設定新功能

private boolean trySetEnabledFunctions(long usbFunctions, boolean forceRestart) {
            // 1. 把新usb功能轉化為字元串
			String functions = null;
			
			// 如果新功能不是NONE,就轉化
            if (usbFunctions != UsbManager.FUNCTION_NONE) {
                functions = UsbManager.usbFunctionsToString(usbFunctions);
            }
            
            // 儲存待設定的新usb功能
            mCurrentFunctions = usbFunctions;
            
            // 如果轉化後的功能為空,那麼就從其它地方擷取
            if (functions == null || applyAdbFunction(functions)
                    .equals(UsbManager.USB_FUNCTION_NONE)) {
                // 擷取persist.sys.usb.config屬性值
                functions = getSystemProperty(getPersistProp(true),
                            UsbManager.USB_FUNCTION_NONE);
                
                // 如果persist.sys.usb.config屬性值還是為NONE
                if (functions.equals(UsbManager.USB_FUNCTION_NONE))
                // 如果adb開啟,傳回adb,否則傳回mtp
                functions = UsbManager.usbFunctionsToString(getChargingFunctions());
            }

            // adb開啟,就追加adb值,否則移除adb值
            functions = applyAdbFunction(functions);

            // 2. 擷取oem覆寫的usb功能
            String oemFunctions = applyOemOverrideFunction(functions);
            
            // 處理非正常啟動模式情況,忽略
            if (!isNormalBoot() && !mCurrentFunctionsStr.equals(functions)) {
                setSystemProperty(getPersistProp(true), functions);
            }
            
            // 3. 設定新功能
            if ((!functions.equals(oemFunctions) 
            && !mCurrentOemFunctions.equals(oemFunctions))
                    || !mCurrentFunctionsStr.equals(functions)
                    || !mCurrentFunctionsApplied
                    || forceRestart) {
                Slog.i(TAG, "Setting USB config to " + functions);
                // 儲存要設定新功能對應的字元串值
                mCurrentFunctionsStr = functions;
                // 儲存oem覆寫功能的字元串值
                mCurrentOemFunctions = oemFunctions;
                mCurrentFunctionsApplied = false;

                // 先斷開已經存在的usb連接配接
                setUsbConfig(UsbManager.USB_FUNCTION_NONE);
                // 判斷是否成功
                if (!waitForState(UsbManager.USB_FUNCTION_NONE)) {
                    Slog.e(TAG, "Failed to kick USB config");
                    return false;
                }

                // 設定新功能,注意,這裡使用的是oem覆寫的功能
                setUsbConfig(oemFunctions);
            
                // 如果新功能包含mtp或ptp,那麼就要更新usb狀态改變廣播
                // 廣播接收者會映射主記憶體的檔案到PC端
                if (mBootCompleted
                        && (containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
                        || containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
                    updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
                }
                
                // 等待新功能設定完畢
                if (!waitForState(oemFunctions)) {
                    Slog.e(TAG, "Failed to switch USB config to " + functions);
                    return false;
                }

                mCurrentFunctionsApplied = true;
            }
            return true;
        }
           

我把這裡的邏輯分為了三步.

第一步,把待設定的USB功能轉化為字元串,有兩種情況

  1. 如果新功能為

    FUNCTION_NONE

    ,那麼轉化後的值從

    persist.sys.usb.config

    擷取,如果擷取值為NONE,就判斷adb是否開啟,如果開啟了,轉化後的值為adb,如果沒有開啟,轉化後的值為mtp。前面分析說過,

    persist.sys.usb.config

    主要包含用于判斷adb是否開啟在值,然後還包含一些廠商定制且用于測試目的的功能。例如,高通項目,這個值可能為

    adb,diag

    ,這個diag就是高通自己的功能。
  2. 如果新功能不為

    FUNCTION_NONE

    ,把直接轉化。例如新功能為

    FUNCTION_MTP

    ,那麼轉化後的字元串為

    mtp

轉化字元串後,根據adb是否開啟,來決定從轉化後的字元串中增加adb屬性還是移除adb屬性。

第二步,擷取oem覆寫的功能。前面說過,預設系統是沒有使用覆寫功能,是以這裡擷取的覆寫後的功能與新功能轉化後的字元串是一樣的。

我在分析代碼的時候,腦海裡一直在想,這個覆寫功能如何使用。根據我的對代碼的分析,唯一的規則就是主要功能不能覆寫。舉個例子,如果新設定的功能的字元串為mtp,那麼覆寫數組中的其中一項元素的值應該是

normal:mtp:mtp,diag

,其中nomral表示正常啟動,mtp表示原始的功能,mtp,diag表示覆寫後的功能,請注意,覆寫後的功能一定要儲存mtp這個主功能。當然這隻是我個人對代碼分析得出的結論,還沒驗證。這裡我要吐槽一下這個功能的設計者,難道寫個例子以及注意事項就這麼難嗎?

第三步,設定新功能。不過設定新功能前,首先要斷開已經存在的連接配接,然後再設定新功能。設定新功能是通過

setUsbConfig()

方法,來看下實作

private void setUsbConfig(String config) {
            // 設定sys.usb.config
            setSystemProperty(USB_CONFIG_PROPERTY, config);
        }
           

震驚!原來就是設定sys.usb.config的屬性值,還記得嗎,在前面的分析中,也解釋過這個屬性值,它就是代表目前設定的usb功能,從這裡就可以得到證明。

這其實也在提示我們,其實可以通過

adb shell setprop

指令設定這個屬性,進而控制usb功能的切換。在實際的工作中,屢試不爽。

設定這個屬性後如何判斷設定成功了呢?這就是

waitForState()

所做的

private boolean waitForState(String state) {
            String value = null;
            for (int i = 0; i < 20; i++) {
                // 擷取sys.usb.stat值
                value = getSystemProperty(USB_STATE_PROPERTY, "");
                // 與剛才設定的sys.usb.config屬性值相比較
                if (state.equals(value)) return true;
                SystemClock.sleep(50);
            }
            return false;
        }
           

說實話,我看到這段代碼,确實吃了一鲸! 這段代碼在1秒内執行20次,擷取

sys.usb.state

屬性值,然後與設定的

sys.usb.config

屬性值相比較,如果相等就表示功能設定成功。

還記得嗎?在前面的分析中,我也解釋過

sys.usb.state

屬性的作用,它代表usb實際的功能,從這裡就可以得到驗證。

那麼現在有一個問題,底層如何實作usb功能切換呢?當然是響應屬性

sys.usb.config

屬性改變,例如我的項目的底層呼應代碼是這樣

on property:sys.usb.config=mtp,adb && property:sys.usb.configfs=0
	# 先寫0
    write /sys/class/android_usb/android0/enable 0
    # 寫序列号
    write /sys/class/android_usb/android0/iSerial ${ro.serialno}
    # 寫vid, pid
    write /sys/class/android_usb/android0/idVendor 05C6
    write /sys/class/android_usb/android0/idProduct 9039
    # 設定USB功能為mtp,adb
    write /sys/class/android_usb/android0/functions mtp,adb
    # 再寫1啟動功能
    write /sys/class/android_usb/android0/enable 1
    # 啟動adb
    start adbd
    # 設定 sys.usb.state屬性值為sys.usb.config的屬性值
    setprop sys.usb.state ${sys.usb.config}
           

根據注釋,你應該就可以很清楚了解這個過程了。

so, 你以為這就完了嗎?還沒呢,如果新設定的功能是MTP或PTP,那麼還要更新廣播呢。

protected void updateUsbStateBroadcastIfNeeded(long functions) {
            // send a sticky broadcast containing current USB state
            Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
                    | Intent.FLAG_RECEIVER_FOREGROUND);
            // 儲存了usb狀态值
            intent.putExtra(UsbManager.USB_CONNECTED, mConnected);
            intent.putExtra(UsbManager.USB_HOST_CONNECTED, mHostConnected);
            intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
            intent.putExtra(UsbManager.USB_DATA_UNLOCKED,
                    isUsbTransferAllowed() && isUsbDataTransferActive(mCurrentFunctions));
            
            // 儲存了要設定的新功能的值,例如設定的是MTP,那麼參數的key為mtp,值為true
            long remainingFunctions = functions;
            while (remainingFunctions != 0) {
                intent.putExtra(UsbManager.usbFunctionsToString(
                        Long.highestOneBit(remainingFunctions)), true);
                remainingFunctions -= Long.highestOneBit(remainingFunctions);
            }

            // 如果狀态沒有改變,就不發送廣播
            if (!isUsbStateChanged(intent)) {
                return;
            }

            // 注意這裡發送的是一個sticky廣播
            sendStickyBroadcast(intent);
            mBroadcastedIntent = intent;
           

注意,這裡發送的是一個sticky廣播。那麼這個廣播的接收者是誰呢?接收這個廣播又做了什麼呢?這就是下一篇文章的内容。

結束

UsbService是usb協定的實作,例如MTP,PTP建構于usb協定上,UsbService就實作了。然而本文是以MTP為主線進行分析的,篇幅不小,但是如果你要開發或定制關于usb功能時,這篇文章不容錯過。