android mtp簡介
經作者同意,轉發我們公司MTP專家同事huirong的一篇文章。大家也可在程序員第5期看到。
MTP in Android
MTP的全稱是Media Transfer Protocol(媒體傳輸協議),它是微軟公司提出的一套媒體文件傳輸協議。Android從3.0開始支持MTP。不過,在今天的智能手機領域內,Google和微軟是一對冤家,為什麼Android中會使用MTP呢?請看下文。
一 背景知識介紹
筆者相信《程序員》雜誌的絕大多數讀者或多或少都使用過MTP。因為早在智能手機普及前,數碼相機和MP3播放器等都使用了MTP的前身PTP(Picture Transfer Protocol)進行媒體文件傳輸。那時,隻要通過USB數據線把它們連接上Windows操作係統,就能在“我的電腦“中見到這些設備了。此後,用戶可以把它們當做U盤一樣使用,例如對其進行目錄、文件的瀏覽和拷貝等操作。
既然可以通過MTP把智能設備當作U盤使用,那麼它和我們常用的USB大容量存儲(USB Mass Storage,簡稱UMS)有何不同呢?
- UMS模式下,PC操作存儲設備的粒度是設備塊(FAT block),而非文件係統。什麼意思?此處舉一個簡單例子。當Android手機通過UMS將sdcard掛載到PC後,PC就擁有對sdcard的絕對控製權。這樣,手機就無法同時訪問sdcard了。這種做法帶來的後果就是Camera或Music程序將因沒有外部存儲空間而提示無法進行操作(注意,有些廠商的手機對此進行過修改,使得Camera能短時間錄製一部分視頻到內部存儲空間)。這也是Android早期版本中一個很明顯的特點。另外,由於PC在操作sdcard時可能弄壞其文件係統,這將導致sdcard重新掛載到手機後不能被識別。
- 如果Android手機的sdcard以MTP模式掛載到PC機上,sdcard的控製權其實還是屬於手機。隻不過智能手機通過MTP協議向PC機構建了一個虛擬文件係統。PC機操作其中的文件時,都會通過標準MTP協議向智能手機發起請求。另外,Android把MTP功能集成在MediaProvider[1]中,其好處是PC機操作(例如拷貝或刪除等)媒體文件時,媒體數據都會及時更新到媒體數據庫中。而UMS模式下,當sdcard掛載回手機後,Android還得花較長時間重新掃描媒體文件以更新媒體數據庫。
MTP的好處還有很多,例如它可判斷PC機拷貝的媒體文件是否受目標手機支持,甚至可以觸發對應的轉碼程序將其轉換成手機支持的格式。不過和UMS相比,MTP也有不足之處:
- 傳輸大文件的速度較慢。
- MTP不能直接修改文件本身。隻能先拷貝到本地修改,完畢後再拷貝回去。
- 除了Windows外,Linux和MacOS對MTP支持還不是很完善。
下麵我們將介紹MTP協議。
1.1 MTP協議介紹
根據協議,MTP的使用者包括兩個部分,分別是Initiator和Responder。如圖1-1所示:
圖1-1 Initiator和Responder圖示
由圖1-1可知:
- Initiator:主要是指USB Host,例如PC機,筆記本等。協議規定所有MTP操作隻能由Initator發起。
- Responder:一般是諸如數碼相機、智能手機等存儲媒體文件的設備。Responder在MTP中的作用就是處理Initator發起的請求。同時,它還會根據自身狀態的變化發送Event以通知Initiator。
注意:後文我們將統一以PC代表Initiator,Android手機代表Responder。
與很多協議一樣,MTP也有自己的協議棧,如圖1-2所示:
圖1-2 MTP協議棧
由圖1-2可知,MTP協議棧由下到上分別是:
- Pyshical Layer(物理層):物理層在MTP協議中用來傳輸數據。目前有三種物理層可供MTP使用。它們分別是USB:其主要特點是傳輸文件,同步媒體文件時速度快,而且可以邊工作邊充電,這是目前用的最多的一種方式;IP:基於IP的MTP(簡稱MTP/IP)將通過UPnP來匹配和發現設備。它是家庭網絡中是最理想的傳輸方式;Bluetooth:MTP/BT是最省電,同時也是速度最慢的一種傳輸方式,用處較少。
- 傳輸層:MTP中,數據傳輸格式遵循PTP協議
- 命令層:實現了MTP協議中的各種命令。
如上文所述,MTP采用命令-應答方式來工作(Initator發送命令給Responder處理,Responser反饋處理結果),這種方式的主要特點有:
- 所有MTP命令均以Package(數據包)的方式在設備兩端進行傳遞。
- Initiator必須接收到前一條消息的處理結果(不論是成功還是超時)後,才能發送下一條消息。
下麵我們將以PC通過MTP打開一個文件為例,按順序介紹其中涉及到幾個主要MTP命令:
- 當設備第一次連接上PC後,Initiator(即PC)首先會發送一個名為GetDeviceInfo的請求以獲取設備的信息,這些信息包括設備所支持PTP版本的程度,以百分號表示(默認是100)、所支持的MTP命令(Operation Supported)、所支持的Event類型等。
- 接著PC端會發送OpenSession命令以創建一個會話,該會話一直保持到設備從PC上斷開為止。此後所有命令(除GetDeviceInfo命令外)必須在此會話存活期間才能發送。會話在MTP協議中由SessionID來標識,它是一個32位的無符號整型,由PC選擇並傳給手機。
- PC端如果要進行文件操作的話,必須從根目錄開始定位目標文件。由於Windows的特殊性,手機內部存儲卡在windows係統中顯示為盤符。注意,如果手機內部有兩塊存儲卡的話(如內部存儲卡和外部sd卡),Windows中會顯示為兩個盤符。PC端需要通過GetStorageIDs命令返回某個盤符對應的StorageID。在MTP中,StorageID是一個32位無符號整型,每一個StorageID代表了一個邏輯盤符。
- PC端可以根據上一步的StorageID號,利用GetStorageInfo操作去獲取存儲設備的信息,例如剩餘存儲空間、文件係統類型、訪問權限等。
- 接著,PC就會通過GetObjectHandles命令來獲取此盤符下的文件和子目錄的Object Handles(一個Object Handle代表一個文件或目錄。該值由Responder生成並保證唯一性)。有了Object Handle,PC就可以操作這些文件或目錄了,例如繼續通過GetObjectHandles獲取某個目錄中子文件和子目錄的信息。
- 假設現在需拷貝一個文件到手機上,那麼PC會通過SendObjectInfo命令將文件信息(如文件名、文件大小)等傳遞給手機。而手機需要檢查目標目錄是否有足夠的空間和對應權限。
- 如果一切正常,PC將通過SendObject把數據傳遞給手機。真正寫文件到設備存儲空間的則是手機中的Responder。Android實現的MTP還會在媒體文件傳輸完畢後,將信息更新到媒體數據庫中。
- 除此之外,PC還可利用SetObjectPropValue 命令來設置文件的各種屬性值,如Audio BitRate(比特率),Sample Rate(采樣率),Number Of Channels(聲道)等。
以上為讀者描述了MTP使用的一個簡單案例。至於其中的各種MTP命令,讀者不妨閱讀參考文獻1,即《MTP Specification v1.0.pdf》。協議對各種命令都有非常精確的描述,例如表1-1,表1-2所示為GetDeviceInfo命令,返回值定義。其參數類型,傳遞方向都有詳細解釋(不得不說,和Linux比起來,微軟的開發/技術文檔做得相當到位)。
表1-1 GetDeviceInfo命令定義
Operation Code |
0x1001 |
GetDeviceInfo對應命令的數字編號是0x1001 |
Data |
DeviceInfo dataset |
手機端返回的設備信息數據集 |
Data Direction |
R->I |
數據傳輸方向是手機到PC |
ResponseCode Options |
OK, Parameter_Not_Supported |
手機給PC的返回值 |
表1-2所示為GetDeviceInfo的返回數據集的定義。
表1-2 GetDeviceInfo返回數據集的定義
Dataset field |
Field order |
Size (bytes) |
Datatype |
Comments |
Standard Version |
1 |
2 |
UINT16 |
手機對PTP協議的支持程度,以%表示,默認是100 |
MTP Vendor Extension ID |
2 |
4 |
UINT32 |
手機對PTP廠商擴展協議的支持,默認是0xFFFFFFFF |
MTP Version |
3 |
2 |
UINT16 |
手機支持的MTP標準的版本,以%表示 |
MTP Extensions |
4 |
Variable |
String |
手機支持的MTP擴展集 |
Functional Mode |
5 |
2 |
UINT16 |
手機允許的模式 |
Operations Supported |
6 |
Variable |
Operation Code Array |
在當前功能模式下,手機支持的所有操作 |
Event Supported |
7 |
Variable |
Event Code Array |
在當前功能模式下,手機能產生的所有事件 |
Device Properties Supported |
8 |
Variable |
Device Property Code Array |
在當前功能模式下,手機支持的所有設備屬性 |
Capture Formats |
9 |
Variable |
Object Format Code Array |
手機可以自己生成的文件格式,不包括拷貝到手機上文件格式 |
Playback Formats |
10 |
Variable |
Object Format Code Array |
手機可以解析和理解的所有格式類型 |
Manufacturer |
11 |
Variable |
String |
人可讀的手機製造商的標識 |
Model |
12 |
Variable |
String |
人可讀的手機型號 |
Device Version |
13 |
Variable |
String |
手機的軟件或固件版本 |
Serial Number |
14 |
Variable |
String |
能標明手機MTP功能的唯一序列號 |
1.2 OS對MTP的支持及認證
MTP協議既然由微軟提出,理所當然,Windows對其支持自然是不遺餘力。目前Windows操作係統中,MTP和多媒體框架緊密結合,並且已經成為Windows Media框架中的重要一部分。如WMP10(Windows Media Player 10)和WMP11均內置對MTP功能,其中WMP11還新增對Playlist和Album art的支持。
微軟除了提出MTP協議並在Windows操作係統中提供大力支持外,它對使用MTP協議的設備也有所管理。所有標稱支持MTP協議的設備,必須通過微軟的測試WLK(Windows Logo Kit)。WLK測試通過的設備可以獲得一個徽標。關於WLK測試的詳細信息,請讀者參考https://msdn.microsoft.com/zh-cn/library/windows/hardware/gg487530.aspx。從以上鏈接中也能下載到wpdmon,它是MTP開發中最常用的測試工具,可顯示出所有PC與手機進行MTP操作時發送的命令、數據及返回值。圖1-3為筆者測試某台Android手機的MTP功能時用wpdmon截獲的信息示意圖:
圖1-3 wpdmon工具使用示意圖
下麵我們來看MTP在Android平台中的實現。
二 Android中的MTP
Android從3.0開始集成MTP功能,主要原因有三個:
- 手機要支持UMS的話,必須有一個sd卡,因為sd卡往往采用Windows支持的分區格式。如果想把內部存儲空間通過UMS掛載到Windows上,則內部存儲空間需采用特定的分區格式。這對某些手機而言根本不可行。因為內部存儲空間本身可能是一個設備,它們采用統一的分區格式。不能因為需要使用UMS,而再增加一塊特定分區格式的存儲設備。
- UMS掛載到PC後,PC操作係統擁有絕對控製權。此時,Android係統將無法操作這些設備。根據前文舉的Camera例子而言,這對越來越高級的Android版本而言是不可接受的。
- 另外一個不可忽略的事實就是Windows操作係統在普通勞動人民那兒依然占據極高的市場份額。這恐怕也是明知Linux、MacOS對MTP支持力度不夠,Android也要集成它的一個重要原因吧。
2.1 Android中MTP的代碼架構
要使用MTP功能,首先需要在設置中啟用USB連接模式為MTP,如圖1-4所示:
圖1-4 Settings中的MTP設置
圖1-4所示為參考機(Android 4.1版本)中“USB連接模式”設置。該操作實際上會觸發USB驅動做相應變動。本文不擬討論其中的過程,讀者可參考手機中init.platform-name.usb.rc文件以查看Android係統中USB的模式設置。從目前市麵上發布的數款Android 4.0及後續版本的機型來看,MTP/PTP大有取代UMS的趨勢。
根據前文所述,Android中的MTP和已有的MediaProvider模塊結合緊密,以更好體現“Media Transfer”的特性。其主要結構如圖1-5所示:
圖1-5 Android MTP架構圖
由圖1-5可知,Android MTP架構由下到上分別是:
- C++層包括幾個主要對象,如MtpRequestPacke負責從USB驅動讀取數據,並結構化命令格式及其參數、MtpDataPacket負責結構化手機要返回給PC的數據包、MtpResponsePacket負責結構化手機要給PC返回的response。MtpServer負責解析來自PC的命令並調用相應的接口函數進行處理。
- Java層包括UsbReceiver、MtpService、MtpServer等對象。其中UsbReceiver用來監視USB事件,判斷何時啟動或停止MtpService。MtpService負責啟動MtpServer和加載存儲設備的信息到數據庫。MtpServer負責通過jni接口去啟動/停止C++層中MtpServer以及處理Storage的添加和刪除。MediaProvider則負責查詢和更新數據庫。MtpDatabase名字雖然叫Database,但實際功能用於在MediaProvider和MtpServer之間轉換數據格式。例如把MTP傳遞過來的信息(如文件大小、文件路徑等)轉換成MediaProvider需要的格式以方便其更新數據庫。
下麵我們來看MTP的工作流程。
2.2 MTP流程分析
我們先來看MTP模塊啟動的流程,如圖1-6所示:
圖1-6 MTP主要模塊啟動流程
由圖1-6可知:
- 當手機連上usb線後,UsbReceiver會收到來自係統的USB_STATE廣播事件。接著它需要從UsbManager中查詢USB的鏈接狀態,MTP的設置信息和PTP的設置信息。當用戶設置為使用MTP模式時,UsbReceiver將通過startService函數啟動MtpService。
- MtpService啟動,在其onStartCommand中將創建MtpDatabase對象和MtpServer對象。
- UsbReceiver同時通過insert一條特殊uri(值為“content://media/none/mtp_connected”)的方式,觸發MdiaProvder調用MtpService的bindService函數。這樣,MediaProvider和MtpService就建立了緊密聯係。
MtpServer是Android平台中MTP協議處理的核心模塊,它會單獨啟動一個線程用於接收PC端的命令,其代碼如圖1-7所示:
圖1-7 MtpServer run函數代碼片段
由圖1-7可知,MtpServer不斷從文件描述符讀取請求,然後調用handleRequest進行處理。最後把處理結果返回給對端。
從這段代碼讀者可以發現,Android MTP命令層和物理層之間的耦合度較低,這樣也方便將來實現MTP/IP功能。
接下來我們看看PC端發送SendObjectInfo的處理流程,如圖1-8所示:
圖1-8 sendObjectInfo處理流程圖
由圖1-8可知SendObjectInfo的處理流程大體步驟如下:
- PC發SendObjectInfo命令給MtpServer。MtpServer需要檢查存儲設備剩餘空間、可支持的最大文件大小。如果一切正常的話,它會通過MediaProvider的insert函數往媒體數據庫中加入一條數據項。
- 接著PC通過SendObject將文件內容傳遞給給MtpServer。而MtpServer就會創建該文件,並把數據寫到文件中。
- 當文件數據發送完畢,MtpServer調用endSendObject。而endObject則會觸發MediaScanner進行媒體文件掃描。當然,掃描完後,該文件攜帶的媒體信息(假如是MP3文件的話,則會把專輯信息、歌手、流派、長度等內容)加入到媒體數據庫中。
通過對SendObjectInfo描述,我們也可看出,Android充分利用了其平台本身的特性,真正將媒體傳輸協議和媒體文件掃描恰到好處得結合起來,從而發揮了MTP最大功效。
三 總結
本文主要對Android中的MTP進行了相關介紹。雖然MTP協議由微軟提供,但因為曆史原因,其使用程度相當廣泛,以至於Android也提供了最基本的MTP實現。
當然,如果要做到真正實用並通過微軟認證,手機廠商還需要在此基礎上做進一步的開發。結合筆者自己的使用經曆,國外大牌手機廠商例如Sony、Samsung、Nokia等對MTP的支持相當到位。相比而言,國內手機廠商的起步稍微晚一點,需要投入更多的精力才能超越。另外,隨著無線技術的普及,MTP基於IP的實現也將極大方麵用戶的使用。筆者在此希望大家能一起努力,早日讓用戶從USB數據線中解放出來。
最後更新:2017-04-03 12:53:49