1. 程式人生 > >MTP模式與USB儲存模式(MTP in Android)

MTP模式與USB儲存模式(MTP in Android)

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測試的詳細資訊,請讀者參考http://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資料線中解放出來。