MTP in Android详解

MTP in Android详解java

      最近好长一段时间没有作笔记了,今天主要学习一下MTP相关的知识。程序员

      MTP的全称是Media Transfer Protocol(媒体传输协议),它是微软公司提出的一套媒体文件传输协议。Android从3.0开始支持MTP。数据库

一  背景知识介绍

笔者相信《程序员》杂志的绝大多数读者或多或少都使用过MTP。由于早在智能手机普及前,数码相机和MP3播放器等都使用了MTP的前身PTP(Picture Transfer Protocol)进行媒体文件传输。那时,只要经过USB数据线把它们链接上Windows操做系统,就能在“个人电脑“中见到这些设备了。此后,用户能够把它们当作U盘同样使用,例如对其进行目录、文件的浏览和拷贝等操做。windows

既然能够经过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中,其好处是PC机操做(例如拷贝或删除等)媒体文件时,媒体数据都会及时更新到媒体数据库中。而UMS模式下,当sdcard挂载回手机后,Android还得花较长时间从新扫描媒体文件以更新媒体数据库。

MTP的好处还有不少,例如它可判断PC机拷贝的媒体文件是否受目标手机支持,甚至能够触发对应的转码程序将其转换成手机支持的格式。不过和UMS相比,MTP也有不足之处:架构

  • 传输大文件的速度较慢。
  • MTP不能直接修改文件自己。只能先拷贝到本地修改,完毕后再拷贝回去。
  • 除了Windows外,Linux和MacOS对MTP支持还不是很完善。

下面咱们将介绍MTP协议。框架

1.1  MTP协议介绍

根据协议,MTP的使用者包括两个部分,分别是Initiator和Responder。如图1-1所示:ide

图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数据线中解放出来。

 

我的以为主要把握两个流程:

1.java层向.cpp层流程,即命令层--->传输层

    主要涉及流程为:UsbReceiver   --> MtpService  -->  MediaProvider -->  MtpDatabase   -->   MtpServer(MTP核心)

2. .c驱动层向.cpp层流程,即物理层-->传输层-->命令层

    主要涉及流程为:UsbDriverSide --------> MtpServer --->MtpStorage --> MtpDatabase --> MediaProvider  -->MediaScaner

                       SendObjectInfo()                                beginSendObject()                                              insert()

                                              SendObject()                                       endSendObject()                                               scanMtpFile()

相关文章
相关标签/搜索