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