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