Android之 MTP框架和流程分析 (1)

原文: http://www.cnblogs.com/skywang12345/p/3474206.html

概要
本文的目的是介绍Android系统中MTP的一些相关知识。主要的内容包括:
第1部分 MTP简介
对Mtp协议进行简单的介绍。
第2部分 MTP框架
介绍Android系统下MTP的框架。
第3部分 MTP启动流程
详细分析MTP服务的启动流程,包括Java层, JNI层, kernel相关知识的介绍。
第4部分 MTP协议之I->R流程
以”PC中打开一个MTP上的文件(读取文件内容)”为例,来对”MTP协议中Initiator到Reponser的流程”进行说明。
第5部分 MTP协议之R->I流程
以”Android设备中将一个文件拷贝到其他目录”来对”MTP协议中Reponser到Initiator的流程”进行说明。
注意:本文的MTP分析的软件环境Android 4.3 + Kernel 3.0!

转载请注明出处:http://www.cnblogs.com/skywang12345/p/3474206.html

第1部分 MTP简介
MTP,全称是Media Transfer Protocol(媒体传输协议)。它是微软的一个为计算机和便携式设备之间传输图像、音乐等所定制的协议。

Android从3.0开始支持MTP。MTP的应用分两种角色,一个是作为Initiator,另一个作为Responder。以”Android平板电脑”连接”PC”为例,他们的关系如图1-01所示。

Initiator—— 在MTP中所有的请求都有Initiator发起。例如,PC请求获取Android平板电脑上的文件数据。

Responder—— 它会处理Initiator的请求;除此之外,Responder也会发送Event事件。

图1-01

注意:关于MTP的详细规格请参考《MTP_Specification_V1.0》!

第2部分 MTP框架
Android中MTP的框架如图2-01所示:

说明:

  在Kernel层,USB驱动负责数据交换,而MTP驱动负责和上层进行通信,同时也和USB驱动进行通信。

(01)USB驱动负责数据交换,是指Android设备和PC通过USB数据线连接之后,实际的数据交换是经过USB数据线发送给USB驱动的。

(02)对于”MTP请求”而言,MTP驱动会从USB驱动中解析出的MTP请求数据,然后传递给上层。而对于上层传来的”MTP反馈”,MTP驱动也会将反馈内容打包好之后,通过传递给USB驱动。

  在JNI层,MtpServer会不断地监听Kernel的消息"MTP请求",并对相应的消息进行相关处理。同时,MTP的Event事件也是通过MtpServer发送给MTP驱动的。MtpStorage对应一个"存储单元";例如,SD卡就对应一个MtpStorage。MtpPacket和MtpEventPacket负责对MTP消息进行打包。android_mtp_MtpServer是一个JNI类,它是"JNI层的MtpServer 和 Java层的MtpServer"沟通的桥梁。android_mtp_MtpDatabase也是一个JNI类,JNI层通过它实现了对MtpDatabase(Framework层)的操作。

  在Framework层,MtpServer相当于一个服务器,它通过和底层进行通信从而提供了MTP的相关服务。MtpDatabase充当着数据库的功能,但它本身并没有数据库对数据进行保存,本质上是通过MediaProvider数据库获取所需要的数据。MtpStorage对应一个"存储单元",它和"JNI层的MtpStorage"相对应。

  在Application层,MtpReceiver负责接收广播,接收到广播后会启动/关闭MtpService;例如,MtpReceiver收到"Android设备 和 PC连上"的消息时,会启动MtpService。MtpService的作用是提供管理MTP的服务,它会启动MtpServer,以及将本地存储内容和MTP的内容同步。MediaProvider在MTP中的角色,是本地存储内容查找和本地内容同步;例如,本地新增一个文件时,MediaProvider会通知MtpServer从而进行MTP数据同步。

第3部分 MTP启动流程
该部分对MTP服务的启动流程进行详细介绍。我们先通过时序图对MTP启动流程有个整体印象,然后再通过代码进行详细分析。其中,涉及的内容,包括Java层、JNI层和Kernel。

MTP服务启动时,Java层的程序流程如下图3-01所示。

图3-01

说明:MTP服务启动的触发事件是”PC和Android设备建立MTP连接”。当她们建立MTP连接时,USB驱动将产生USB连接消息,并最终通知UsbManager。UsbManager发出广播,并且广播被MtpReceiver收到;MtpReceiver收到广播后会启动MtpService,同时通知MediaProvider。MediaProvider会与MtpService绑定,若Android设备中的文件结构有变化(如”新键文件”),MediaProvider则会通知MtpService。MtpService启动后会创建MtpDatabase;之后,还会创建MtpServer,MtpServer会和MtpDatabase关联。然后,MtpService会遍历本地的存储设备,并建立相应的MtpStorage,并将该MtpStorage添加到MtpDatabase和MtpServer中。最后,MtpService会启动MtpServer。

MTP服务启动时,JNI层的程序流程如下图3-02所示。

图3-02

说明: 前面说过MtpService启动后会先后创建MtpDatabase对象和MtpServer对象(Java层),然后启动MtpServer(Java层)。

在创建MtpDatabase对象时,会通过native_setup()调用JNI本地方法。目的是进行初始化,为后面的MtpServer调用做准备。

在创建MtpServer对象(Java层)时,会通过native_setup()调用JNI本地方法。在本地方法中,打开MTP驱动创建的文件节点”/dev/mtp_usb”,并会获取MyMtpDatabase对象,然后创建”MtpServer对象(JNI层)”。

在启动MtpServer线程时,会对应的执行MtpServer(JNI层)的run()方法。MtpServer(JNI层)的run()中会不断的从”/dev/mtp_usb”中读取数据,并进行相应的处理。

涉及到的主要文件的路径:

复制代码
packages/providers/MediaProvider/src/com/android/providers/media/MtpReceiver.java
packages/providers/MediaProvider/src/com/android/providers/media/MtpService.java
packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
frameworks/base/media/java/android/mtp/MtpServer.java
frameworks/base/media/java/android/mtp/MtpDatabase.java
frameworks/base/media/java/android/mtp/MtpStorage.java
frameworks/base/media/jni/android_mtp_MtpServer.cpp
frameworks/base/media/jni/android_mtp_MtpDatabase.cpp
frameworks/av/media/mtp/MtpServer.h
frameworks/av/media/mtp/MtpServer.cpp
frameworks/av/media/mtp/MtpDatabase.h
复制代码

接下来,通过代码对MTP的服务启动的各个流程进行分析

1 USB_STATE广播
USB_STATE广播,即”android.hardware.usb.action.USB_STATE”广播。它是在USB连上/断开时,由UsbManager发出的广播;MtpReceive会接收该广播并进行处理。

例如,当”Android设备”和”PC”通过USB连接时,MtpReceiver会接收到USB_STATE广播,并判断”USB是不是连上,MTP是不是Enable状态”从而决定是否启动MtpService。

1.1 MtpReceiver监听广播的注册
MtpReceiver.java在它对应的manifest中注册监听”android.intent.action.BOOT_COMPLETED” 和 “android.hardware.usb.action.USB_STATE” 监听。

packages/providers/MediaProvider/AndroidManifest.xml中的源码如下:

复制代码
1
2
3
4
5
6
7
8
复制代码
说明:

这是采用静态方式注册的广播,经过上面的注册之后,MtpReceiver就可以接收”android.intent.action.BOOT_COMPLETED” 和 “android.hardware.usb.action.USB_STATE” 这两个广播了。

(01) “android.intent.action.BOOT_COMPLETED” – 是Android设备开机完成后发出的广播。

    MtpReceiver通过该广播,来处理开机时Android设备和PC就已经是连接状态的情况。

    该字符串对应是frameworks/base/core/java/android/content/Intent.java中的ACTION_BOOT_COMPLETED变量,源码如下:

public static final String ACTION_BOOT_COMPLETED = “android.intent.action.BOOT_COMPLETED”;
(02) “android.hardware.usb.action.USB_STATE” – 是USB连接状态发生变化时产生的广播。

   MtpReceiver通过该广播,来处理Android设备和PC之间通过USB线热插拔的情况。

   该字符串对应是frameworks/base/core/java/android/hardware/usb/UsbManager.java中的ACTION_USB_STATE变量,源码如下:

public static final String ACTION_USB_STATE = “android.hardware.usb.action.USB_STATE”;

1.2 MtpReceiver对广播的处理
MtpReceiver对广播的处理在MtpReceiver.java中实现,源码如下:

复制代码
1 public void onReceive(Context context, Intent intent) {
2 final String action = intent.getAction();
3 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
4 final Intent usbState = context.registerReceiver(
5 null, new IntentFilter(UsbManager.ACTION_USB_STATE));
6 if (usbState != null) {
7 handleUsbState(context, usbState);
8 }
9 } else if (UsbManager.ACTION_USB_STATE.equals(action)) {
10 handleUsbState(context, intent);
11 }
12 }
复制代码
说明:

MtpReceiver的onReceive()中会处理Intent.ACTION_BOOT_COMPLETED 和 UsbManager.ACTION_USB_STATE 这两个广播。

Intent.ACTION_BOOT_COMPLETED 和 UsbManager.ACTION_USB_STATE 的处理流程一样,最终都是通过handleUsbState()来处理的。下面的是基于UsbManager.ACTION_USB_STATE广播。

当”Android设备”和”PC”连接时,Android系统会检测USB连接事件并发出UsbManager.ACTION_USB_STATE广播。MtpReceiver最终会调用handleUsbState()对该广播进行处理。

1.3 MtpReceiver的handleUsbState()
handleUsbState()也在MtpReceiver.java中实现,源码如下:

复制代码
1 private void handleUsbState(Context context, Intent intent) {
2 Bundle extras = intent.getExtras();
3 boolean connected = extras.getBoolean(UsbManager.USB_CONFIGURED); // 获取USB的连接状态
4 boolean mtpEnabled = extras.getBoolean(UsbManager.USB_FUNCTION_MTP); // 获取MTP的Enable状态
5 boolean ptpEnabled = extras.getBoolean(UsbManager.USB_FUNCTION_PTP); // 获取PTP的Enable状态
6
7 if (connected && (mtpEnabled || ptpEnabled)) {
8 // 如果USB是连接状态,并且“MTP或者PTP是Enable状态”就执行下面的代码
9
10 intent = new Intent(context, MtpService.class);
11 if (ptpEnabled) {
12 intent.putExtra(UsbManager.USB_FUNCTION_PTP, true);
13 }
14 // 启动MtpService服务
15 context.startService(intent);
16 // 通知MediaProvider,MTP已经连上。
17 context.getContentResolver().insert(Uri.parse(
18 “content://media/none/mtp_connected”), null);
19 } else {
20 // 结束MtpService服务
21 context.stopService(new Intent(context, MtpService.class));
22 // 通知MediaProvider,MTP已经断开。
23 context.getContentResolver().delete(Uri.parse(
24 “content://media/none/mtp_connected”), null, null);
25 }
26 }
复制代码
说明:

handleUsbState()会先获取”USB连接状态”,”MTP和PTP的Enable”状态。

如果USB是连上的,并且MTP或PTP是Enable,则启动MtpService,并通知MediaProvider。

否则的话,则终止MtpService,并通知MediaProvider。

小结:MtpReceiver会监听”Android设备开机完成广播” 和 “USB连接/断开广播”的处理。到收到广播时,会根据”USB的连接状态,MTP/PTP的Enable状态”决定对MTP的处理。如果是连上状态,而且MTP服务是Enable的,则启动MtpService服务;并且通知MediaProvider。

2 startService()
在”Android设备与PC连上,并且MTP是Enable”的情况下,MtpService会被启动。在”Android设备与PC断开”时,MtpService会被终止。

MtpService的作用是提供管理MTP的服务。例如,MtpService启动时,它会遍历Android设备上所有的存储设备,如果该存储设备是挂载的,则创建该存储设备对应的MtpStorage对象,并将该MtpStorage对象添加到MtpDatabase和MtpServer中。在Android设备中存储结构发生变化时,会收到MediaProvider发来的消息,进而将消息转发给MtpServer,进行MTP同步。

下面,通过代码对MtpService进行介绍。

2.1 创建MtpService
MtpService继承于Service,这意味着它是一个服务。根据服务的执行流程,MtpService在创建后会执行onCreate()函数。

MtpService中onCreate()的源码如下:

复制代码
1 public class MtpService extends Service {
2
3 @Override
4 public void onCreate() {
5 // 监听“屏幕解锁”广播
6 registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_PRESENT));
7
8 // 获取StorageManager对象。根据静态工厂方法获取的。
9 mStorageManager = StorageManager.from(this);
10 synchronized (mBinder) {
11 // 根据“屏幕锁定与否”来启动/禁用Mtp功能。
12 // 如果是当前用户在设定了屏幕解锁密码的情况下锁屏,则禁用Mtp功能。
13 updateDisabledStateLocked();
14 // 监听“存储设备的挂载/卸载等广播事件”。
15 mStorageManager.registerListener(mStorageEventListener);
16 // 遍历“Android设备”上所有存储设备。
17 // 如果该存储设备是“挂载状态(MEDIA_MOUNTED)”,则通过Mtp锁定该存储设备;
18 // 这里的Mtp锁定,是指Mtp能识别到该存储设备,并将该存储设备映射到PC上。
19 StorageVolume[] volumes = mStorageManager.getVolumeList();
20 mVolumes = volumes;
21 for (int i = 0; i < volumes.length; i++) {
22 String path = volumes[i].getPath();
23 String state = mStorageManager.getVolumeState(path);
24 if (Environment.MEDIA_MOUNTED.equals(state)) {
25 volumeMountedLocked(path);
26 }
27 }
28 }
29 }
30
31 …
32 }
复制代码
说明:

(01) 如果当前用户在设定了”屏幕锁定密码”的情况下将Android设备锁屏,此时MTP功能是被禁用掉的。

   updateDisabledStateLocked()的作用,就是处理这种情况的。如果"用户不是当前用户" 或者 "用户在设定了'屏幕锁定密码'的情况下将Android设备锁屏";此时MTP功能都是被禁用了的。

   mReceiver是处理解锁的广播。当屏幕锁解除之后,MTP又能恢复正常工作!

(02) MTP是将Android设备的存储设备映射到PC上。因此,Android设备上的存储设备如果被用户卸载掉的话,要通知PC;而mStorageEventListener就是来监听”Android设备上的存储设备的挂载/卸载状态”的。

2.2 volumeMountedLocked()
volumeMountedLocked()在MtpService.java中实现,源码如下:

复制代码
1 private void volumeMountedLocked(String path) {
2 // 忽略“U盘”
3 if(MediaProvider.UDISK_MOUNT_POINT.equals(path))
4 return;
5 // 在所有的存储设备中遍历,找出该“path对应的存储设备”,并将它添加到mVolumeMap中。
6 for (int i = 0; i < mVolumes.length; i++) {
7 StorageVolume volume = mVolumes[i];
8 if (volume.getPath().equals(path)) {
9 long reserveSpace = volume.getMtpReserveSpace() * 1024 * 1024;
10 if(path.equals(MediaProvider.LOCAL_MOUNT_POINT))
11 volume.setStorageId(0);
12 else if(path.equals(MediaProvider.UDISK_MOUNT_POINT)){
13 volume.setStorageId(2);
14 }else{
15 volume.setStorageId(1);
16 }
17 mVolumeMap.put(path, volume);
18 if (!mMtpDisabled) {
19 if (volume.isPrimary() || !mPtpMode) {
20 addStorageLocked(volume);
21 }
22 }
23 break;
24 }
25 }
26 }
复制代码
说明:

虽然 volumeMountedLocked()调用addStorageLocked()。但此时没有进行实质性的动作,真正映射的工作是在onStartCommand()中完成的,即在服务启动之后完成的。

2.3 onStartCommand()
由于MtpService是”Started Service”类型的服务,而不是”Bound Service”。所以,MtpService启动之后会执行onStartCommand()。

MtpService.java中onStartCommand()的源码如下:

复制代码
1 public int onStartCommand(Intent intent, int flags, int startId) {
2 synchronized (mBinder) {
3 // 根据“屏幕锁定与否”来启动/禁用Mtp功能。
4 // 如果是当前用户在设定了屏幕解锁密码的情况下锁屏,则禁用Mtp功能。
5 updateDisabledStateLocked();
6 mPtpMode = (intent == null ? false
7 : intent.getBooleanExtra(UsbManager.USB_FUNCTION_PTP, false));
8 String[] subdirs = null;
9 if (mPtpMode) {
10 // PTP模型才执行到这里。
11 int count = PTP_DIRECTORIES.length;
12 subdirs = new String[count];
13 for (int i = 0; i < count; i++) {
14 File file =
15 Environment.getExternalStoragePublicDirectory(PTP_DIRECTORIES[i]);
16 // make sure this directory exists
17 file.mkdirs();
18 subdirs[i] = file.getPath();
19 }
20 }
21 // 获取“主存储分区”。
22 final StorageVolume primary = StorageManager.getPrimaryVolume(mVolumes);
23 // 新建MtpDatabase对象
24 mDatabase = new MtpDatabase(this, MediaProvider.LOCAL_VOLUME,
25 primary.getPath(), subdirs);
26 manageServiceLocked();
27 }
28
29 return START_STICKY;
30 }
复制代码
说明:onStartCommand()中创建了mDatabase对象,然后调用manageServiceLocked()。

2.4 manageServiceLocked()
该函数在MtpService.java中实现,源码如下:

复制代码
1 private void manageServiceLocked() {
2 // 是不是当前用户
3 final boolean isCurrentUser = UserHandle.myUserId() == ActivityManager.getCurrentUser();
4 if (mServer == null && isCurrentUser) {
5 // 新建mServer对象
6 mServer = new MtpServer(mDatabase, mPtpMode);
7 // 如果MTP没被禁用调,则调用addStorageDevicesLocked()
8 if (!mMtpDisabled) {
9 addStorageDevicesLocked();
10 }
11 // 启动MtpServer
12 mServer.start();
13 } else if (mServer != null && !isCurrentUser) {
14 mServer = null;
15 }
16 }
复制代码
说明:manageServiceLocked()会新建MtpServer对象,在通过addStorageDevicesLocked()将存储设备添加到Mtp上之后,再启动MtpServer。MtpService会启动一个线程用于管理Android设备和PC之间的通信,它也会”将通过addStorageDevicesLocked()添加的存储设备”映射到PC上。

2.5 addStorageDevicesLocked()
该函数在MtpService.java中实现,源码如下:

复制代码
1 private void addStorageDevicesLocked() {
2 if (mPtpMode) {
3 final StorageVolume primary = StorageManager.getPrimaryVolume(mVolumes);
4 final String path = primary.getPath();
5 if (path != null) {
6 String state = mStorageManager.getVolumeState(path);
7 if (Environment.MEDIA_MOUNTED.equals(state)) {
8 addStorageLocked(mVolumeMap.get(path));
9 }
10 }
11 } else {
12 // 如果是MTP模式,则调用addStorageLocked(),mVolumeMap中的存储设备添加到Mtp中。
13 for (StorageVolume volume : mVolumeMap.values()) {
14 addStorageLocked(volume);
15 }
16 }
17 }
复制代码
说明: 如果是MTP模式,则调用addStorageLocked(),mVolumeMap中的存储设备添加到Mtp中。mVolumeMap在volumeMountedLocked()中已经被初始化,它保存的是挂载状态的存储设备。

2.6 addStorageLocked()
该函数在MtpService.java中实现,源码如下:

复制代码
1 该函数在MtpService.java中实现,源码如下:
2 private void addStorageLocked(StorageVolume volume) {
3 // 忽略 “volume为空” 或者 “volume是u盘”的情况
4 if(volume != null && MediaProvider.UDISK_MOUNT_POINT.equals(volume.getPath()))
5 return;
6 // 新建该“存储设备”对应的MtpStorage,并将该“存储设备”添加到哈希表mStorageMap中。
7 MtpStorage storage = new MtpStorage(volume, getApplicationContext());
8 String path = storage.getPath();
9 mStorageMap.put(path, storage);
10
11 // 将storage添加到mDatabase中
12 if (mDatabase != null) {
13 mDatabase.addStorage(storage);
14 }
15 // 将storage添加到mServer中
16 if (mServer != null) {
17 mServer.addStorage(storage);
18 }
19 }
复制代码

小结:

MtpService服务的主要工作是搜索出Android设备上所有”挂载”的存储设备,然后根据这些挂载的存储设备分别创建MtpStorage对象;随后,将MtpStorage对象添加到MtpDatabase中进行数据转换和同步,同时也将MtpStorage添加MtpServer,随后的”Android设备和PC之间的通信和数据同步等工作”就交由MtpServer主导进行。

3 insert(“mtp_connected”)
MtpReceiver在handleUsbState()通过insert()将消息上报给MediaProvider。

3.1 MediaProvider静态注册块
MediaProvider在静态块中添加了对”mtp_connected”事件的监听。源码如下:

复制代码
1 static
2 {
3 …
4
5 URI_MATCHER.addURI(“media”, “*/mtp_connected”, MTP_CONNECTED);
6
7 …
8 }
复制代码

3.2 MediaProvider中的insert()
当MtpReceiver给MediaProvider发出”插入的mtp_connected”消息时,MediaProvider会执行insert()函数。源码如下:

复制代码
1 @Override
2 public Uri insert(Uri uri, ContentValues initialValues) {
3 int match = URI_MATCHER.match(uri);
4
5 ArrayList notifyRowIds = new ArrayList();
6 Uri newUri = insertInternal(uri, match, initialValues, notifyRowIds);
7 notifyMtp(notifyRowIds);
8
9 // we will signal instead after file transfer is successful.
10 if (newUri != null && match != MTP_OBJECTS) {
11 getContext().getContentResolver().notifyChange(uri, null);
12 }
13 return newUri;
14 }
复制代码
说明:insert()会调用insertInternal()对消息进行处理。

3.3 MediaProvider中的insertInternal()
MediaProvider对插入消息的处理在insertInternal()中执行,它的源码如下:

复制代码
1 private Uri insertInternal(Uri uri, int match, ContentValues initialValues,
2 ArrayList notifyRowIds) {
3 …
4
5 switch (match) {
6 case MTP_CONNECTED:
7 synchronized (mMtpServiceConnection) {
8 if (mMtpService == null) {
9 Context context = getContext();
10 // 将MediaProvider和MtpService绑定。
11 context.bindService(new Intent(context, MtpService.class),
12 mMtpServiceConnection, Context.BIND_AUTO_CREATE);
13 }
14 }
15 break;
16 …
17 }
18
19 …
20 }
复制代码
说明:insertInternal()会调用bindService()将MediaProvider和MtpService绑定。

4 bindService()
在MediaProvider的insertInternal()中会调用bindService(),而bindService()则会将MediaProvider和MtpService绑定。

而之所以要绑定,是为了将实现MTP同步。例如,当Android设备上新建一个文件时,最终后同步到MediaProvider数据库中;而MediaProvider数据库看同步完成之后,会发送消息给MtpService通知它进行MTP的同步。

小结:MediaProvider在MtpService启动时和MtpService绑定,在MtpService终止时解除绑定。而绑定的目的是为了实现MTP同步功能。

5 mDatabase=new MtpDatabase()
在MtpService的onStartCommand()中,会通过new MtpDatabase()创建MtpDatabase对象。

MtpDatabase在MTP中,充当着数据库的功能。但它本身并没有数据库对数据进行保存,本质上是通过MediaProvider数据库获取所需要的数据。例如,当在PC上,需要读取某个文件时,MtpDatabase会在MediaProvider数据库中查询出文件的相关信息(包括文件名、大小、扩展名等);然后将这些信息交给MtpServer,MtpServer将消息传递给JNI,在JNI中会通过文件名打开,然后再文件句柄等信息传递给Kernel;Kernel根据文件句柄读取文件信息,并传给PC。

下面,通过代码查看以下MtpDatabase的流程。先看MtpDatabase构造函数,源码如下:

复制代码
1 public class MtpDatabase {
2
3 public MtpDatabase(Context context, String volumeName, String storagePath,
4 String[] subDirectories) {
5 // 调用JNI函数
6 native_setup();
7
8 // 初始化
9 mContext = context;
10 mPackageName = context.getPackageName();
11 mMediaProvider = context.getContentResolver().acquireProvider(“media”);
12 mVolumeName = volumeName;
13 mMediaStoragePath = storagePath;
14 mObjectsUri = Files.getMtpObjectsUri(volumeName);
15 mMediaScanner = new MediaScanner(context);
16
17 mSubDirectories = subDirectories;
18
19 …
20
21 // 初始化设备属性,将其保存到SharedPreferences中
22 initDeviceProperties(context);
23 }
24
25 …
26 }
复制代码
说明:MtpDatabase的构造函数主要进行的是初始化工作,它首先会调用native_setup()。

5.1 native_setup()
native_setup()在MtpDatabase.java中是一个本地方法。它的相关定义如下:

1 static {
2 System.loadLibrary(“media_jni”);
3 }
4 private native final void native_setup();
说明:

从中可以看出native_setup()的实现在libmedia_jni.so中,准确的说是在android_mtp_MtpDatabase.cpp中的注册。相关的代码如下:

1 static JNINativeMethod gMtpDatabaseMethods[] = {
2 {“native_setup”, “()V”, (void *)android_mtp_MtpDatabase_setup},
3 {“native_finalize”, “()V”, (void *)android_mtp_MtpDatabase_finalize},
4 };
从中,我们看出,native_setup()实际上是和JNI中的android_mtp_MtpDatabase_setup()对应。android_mtp_MtpDatabase_setup()的源码如下:

复制代码
1 static void android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz)
2 {
3 // 新建MyMtpDatabase对象database
4 MyMtpDatabase* database = new MyMtpDatabase(env, thiz);
5 // 将database对象保存“field_context”域中。
6 env->SetIntField(thiz, field_context, (int)database);
7 checkAndClearExceptionFromCallback(env, FUNCTION);
8 }
复制代码
说明:

android_mtp_MtpDatabase_setup()会创建一个MyMtpDatabase对象,并将该对象保存”field_context”域中。这个被保存的MyMtpDatabase对象在后面会被用到。

5.2 database=new MyMtpDatabase()
MyMtpDatabase位于”JNI层”,它与”Java层的MtpDatabase”对应。MTP通过调用MyMtpDatabase的接口,给Java层的MtpDatabase发送消息;从而进行相关MTP数据的收集。

小结:MtpDatabase的相当于MTP的数据库。在MtpDatabase的创建过程中,它最终会调用JNI本地方法,创建一个MyMtpDatabase对象,并将该对象保存在域field_context中。MTP通过调用保存在field_context域中的MyMtpDatabase对象,从而调用MtpDatabase,进而获取相关的数据。

6 mServer=new MtpServer()
MtpServer是一个实现Runnable接口,它相当于一个线程;并且在MtpService中被启动。

MtpServer在MTP的Framework层中,充当着服务器的角色。例如,当MTP服务启动时,它会通知底层;当Android设备中新增文件时,它会收到MtpService的消息,并将该消息转发给底层。

MtpServer的构造函数源码如下:

复制代码
1 public class MtpServer implements Runnable {
2 public MtpServer(MtpDatabase database, boolean usePtp) {
3 native_setup(database, usePtp);
4 }
5
6 …
7 }
复制代码
说明:MtpServer实现了Runnable接口,在它的构造函数中,它会调用native_setup()本地方法。在MtpServer中,声明了许多native方法,它们的相关代码如下:

复制代码
1 static {
2 System.loadLibrary(“media_jni”);
3 }
4 private native final void native_setup(MtpDatabase database, boolean usePtp);
5 private native final void native_run();
6 private native final void native_cleanup();
7 private native final void native_send_object_added(int handle);
8 private native final void native_send_object_removed(int handle);
9 private native final void native_add_storage(MtpStorage storage);
10 private native final void native_remove_storage(int storageId);
复制代码

6.1 native_setup()
MtpServer中的native方法在android_mtp_MtpServer.cpp中注册,注册表格如下:

复制代码
1 static JNINativeMethod gMethods[] = {
2 {“native_setup”, “(Landroid/mtp/MtpDatabase;Z)V”,
3 (void *)android_mtp_MtpServer_setup},
4 {“native_run”, “()V”, (void *)android_mtp_MtpServer_run},
5 {“native_cleanup”, “()V”, (void *)android_mtp_MtpServer_cleanup},
6 {“native_send_object_added”, “(I)V”, (void *)android_mtp_MtpServer_send_object_added},
7 {“native_send_object_removed”, “(I)V”, (void *)android_mtp_MtpServer_send_object_removed},
8 {“native_add_storage”, “(Landroid/mtp/MtpStorage;)V”,
9 (void *)android_mtp_MtpServer_add_storage},
10 {“native_remove_storage”, “(I)V”, (void *)android_mtp_MtpServer_remove_storage},
11 };
复制代码
从中,我们直到native_setup()实际上是与android_mtp_MtpServer_setup()对应。

android_mtp_MtpServer_setup()的源码如下:

复制代码
1 static void android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jboolean usePtp)
2 {
3 // 打开文件“/dev/mtp_usb”
4 int fd = open(“/dev/mtp_usb”, O_RDWR);
5 if (fd >= 0) {
6 // 根据“fd”和“MtpDatabase”创建MtpServer对象server
7 MtpServer* server = new MtpServer(fd, getMtpDatabase(env, javaDatabase),
8 usePtp, AID_MEDIA_RW, 0664, 0775);
9 // 将server对象保存到“field_MtpServer_nativeContext”域中。
10 env->SetIntField(thiz, field_MtpServer_nativeContext, (int)server);
11 } else {
12 ALOGE(“could not open MTP driver, errno: %d”, errno);
13 }
14 }
复制代码
说明:

(01) fd是文件”/dev/mtp_usb”的句柄。实际上,MTP是通过”/dev/mtp_usb”去监听PC的请求和向PC发送数据的。

(02) getMtpDatabase(env, javaDatabase)返回的是MtpDatabase对象。

(03) 根据fd和getMtpDatabase()返回的MtpDatabase对象,创建server对象;然后通过SetIntFiel()将server对象保存到field_MtpServer_nativeContext这个域中。

6.2 fd = open(“/dev/mtp_usb”)
android_mtp_MtpServer_setup()会打开”/dev/mtp_usb”文件。在MTP中,MtpServer会不断的从”/dev/mtp_usb”去读取数据来监听PC的请求;同时,数据反馈和其他事件也是通过”/dev/mtp_usb”去反馈给Kernel的。

6.3 getMtpDatabase()
该函数在android_mtp_MtpDatabase.cpp中实现,源码如下:

1 MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
2 return (MtpDatabase *)env->GetIntField(database, field_context);
3 }
说明:field_context在前面介绍的”android_mtp_MtpDatabase_setup()”中被初始化。所以,这里实际上返回的是MyMtpDatabase对象。

6.4 new MtpServer(fd, …)
MtpServer的构造函数在MtpServer.cpp中实现,源码如下:

复制代码
MtpServer::MtpServer(int fd, MtpDatabase* database, bool ptp,
int fileGroup, int filePerm, int directoryPerm)
: mFD(fd),
mDatabase(database),
mPtp(ptp),
mFileGroup(fileGroup),
mFilePermission(filePerm),
mDirectoryPermission(directoryPerm),
mSessionID(0),
mSessionOpen(false),
mSendObjectHandle(kInvalidObjectHandle),
mSendObjectFormat(0),
mSendObjectFileSize(0) {}
复制代码
说明:

其中比较重要的两则信息:(01) mFD是”/dev/mtp_usb”的文件句柄。 (02) mDatabase是上一步getMtpDatabase()返回的MtpDatabase对象。

7 storage = new MtpStorage()
一个MtpStorage对象代表一个MTP存储单元。当Android设备和PC连上时,可能有几个存储单元:例如,内部存储分区,SD卡分区等。

MtpStorage的构造函数如下:

复制代码
1 public class MtpStorage {
2 private final int mStorageId;
3 private final String mPath;
4 private final String mDescription;
5 private final long mReserveSpace;
6 private final boolean mRemovable;
7 private final long mMaxFileSize;
8
9 public MtpStorage(StorageVolume volume, Context context) {
10 // 存储设备ID
11 mStorageId = volume.getStorageId();
12 // 对应“Android设备”上的存储路径
13 mPath = volume.getPath();
14 // 描述
15 mDescription = context.getResources().getString(volume.getDescriptionId());
16 // (对MTP而言)可用空间
17 mReserveSpace = volume.getMtpReserveSpace() * 1024L * 1024L;
18 // 是否可移除
19 mRemovable = volume.isRemovable();
20 // 最大文件大小。(0表示无限制)
21 mMaxFileSize = volume.getMaxFileSize();
22 }
23
24 …
25 }
复制代码

8 mDatabase.addStorage(storage)
MtpDatabase.java中addStorage()的源码如下:

public void addStorage(MtpStorage storage) {
mStorageMap.put(storage.getPath(), storage);
}
public void removeStorage(MtpStorage storage) {
mStorageMap.remove(storage.getPath());
}
说明:mStorageMap是个HashMap对象。此处,addStorage()的作用就是将”存储设备的信息保存到哈希表中”。当该存储设备被卸载时,会调用MtpDatabase.java的removeStorage(),进而从mStorageMap中删除相应的存储设备。

9 mServer.addStorage(storage)
mServer.addStorage(storage) 会对应执行MtpServer.java中的addStorage()函数。源码如下:

1 public void addStorage(MtpStorage storage) {
2 native_add_storage(storage);
3 }
根据前面介绍的gMethods表格,我们知道native_add_storage()实际上是调用的android_mtp_MtpServer_add_storage()。

9.1 android_mtp_MtpServer_add_storage()
复制代码
1 static void android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage)
2 {
3 Mutex::Autolock autoLock(sMutex);
4
5 // 获取MtpServer对象
6 MtpServer* server = getMtpServer(env, thiz);
7 if (server) {
8 // field_MtpStorage_storageId 和 “MtpStorage.java中的mStorageId” 对应
9 jint storageID = env->GetIntField(jstorage, field_MtpStorage_storageId);
10 // field_MtpStorage_path 和 “MtpStorage.java中的mPath” 对应
11 jstring path = (jstring)env->GetObjectField(jstorage, field_MtpStorage_path);
12 // field_MtpStorage_description 和 “MtpStorage.java中的mDescription” 对应
13 jstring description = (jstring)env->GetObjectField(jstorage, field_MtpStorage_description);
14 // field_MtpStorage_reserveSpace 和 “MtpStorage.java中的mReserveSpace” 对应
15 jlong reserveSpace = env->GetLongField(jstorage, field_MtpStorage_reserveSpace);
16 // field_MtpStorage_removable 和 “MtpStorage.java中的mRemovable” 对应
17 jboolean removable = env->GetBooleanField(jstorage, field_MtpStorage_removable);
18 // field_MtpStorage_maxFileSize 和 “MtpStorage.java中的mMaxFileSize” 对应
19 jlong maxFileSize = env->GetLongField(jstorage, field_MtpStorage_maxFileSize);
20
21 // 将“jstring类型的path”转换为“C语言中的char *类型”
22 const char *pathStr = env->GetStringUTFChars(path, NULL);
23 if (pathStr != NULL) {
24 // 获取“存储设备”的描述字符串
25 const char *descriptionStr = env->GetStringUTFChars(description, NULL);
26 if (descriptionStr != NULL) {
27 // 创建(MtpStorage.cpp)MtpStorage对象
28 MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr,
29 reserveSpace, removable, maxFileSize);
30 // 将该MtpStorage对象,添加到MtpServer中
31 server->addStorage(storage);
32 env->ReleaseStringUTFChars(path, pathStr);
33 env->ReleaseStringUTFChars(description, descriptionStr);
34 } else {
35 env->ReleaseStringUTFChars(path, pathStr);
36 }
37 }
38 } else {
39 ALOGE(“server is null in add_storage”);
40 }
41 }
复制代码
说明:

(01) getMtpServer()返回的是MtpServer对象。

(02) 通过GetIntField(), GetObjectField()等一系列操作,将相关的域分别和MtpStorage.java中的域对应起来。

(03) 创建MtpStorage对象。

(04) 通过addStorage()将MtpStorage对象,添加到MtpServer中。

9.2 server=getMtpServer()
getMtpServer()在android_mtp_MtpServer.cpp中实现,源码如下:

1 static inline MtpServer* getMtpServer(JNIEnv *env, jobject thiz) {
2 return (MtpServer*)env->GetIntField(thiz, field_MtpServer_nativeContext);
3 }
说明:getMtpServer()返回的是MtpServer对象。该对象,我们在前面介绍的”android_mtp_MtpServer_setup()”的初始化的。

9.3 storage=new MtpStorage()
在android_mtp_MtpServer_add_storage()中,我们通过GetIntField(),GetObjectField(), GetLongField()等方法获取”JNI中的域”对应的”MtpStoarge.java中的类成员”。下面以field_MtpStorage_storageId为例,简单说说”JNI中的域”是如何与”MtpStorage.java中的mStorageId”关联的。

首先,field_MtpStorage_storageId的值是在register_android_mtp_MtpServer()中初始化的。源码如下:

复制代码
1 int register_android_mtp_MtpServer(JNIEnv *env)
2 {
3 jclass clazz;
4
5 // 找到java中“android.mtp.MtpStorage.java”包
6 clazz = env->FindClass(“android/mtp/MtpStorage”);
7 …
8
9 // 获取“MtpStorage.java中mStorageId”的fieldId,并保存到field_MtpStorage_storageId中
10 field_MtpStorage_storageId = env->GetFieldID(clazz, “mStorageId”, “I”);
11 if (field_MtpStorage_storageId == NULL) {
12 ALOGE(“Can’t find MtpStorage.mStorageId”);
13 return -1;
14 }
15
16 ..
17 }
复制代码

register_android_mtp_MtpServer()又是何时被调用的呢?

它是在android_media_MediaPlayer.cpp的JNI_OnLoad()中被调用的,而JNI_OnLoad是我们调用JNI对应的库时被JNI自动调用执行的。

复制代码
1 jint JNI_OnLoad(JavaVM* vm, void* reserved)
2 {
3 …
4
5 if (register_android_mtp_MtpServer(env) < 0) {
6 ALOGE(“ERROR: MtpServer native registration failed”);
7 goto bail;
8 }
9
10 …
11 }
复制代码

由于在register_android_mtp_MtpServer()中,”MtpStorage.java中mStorageId”的已经保存field_MtpStorage_storageId中;

在android_mtp_MtpServer_add_storage()中,我们就可以通过env->GetIntField(jstorage, field_MtpStorage_storageId)来获取”MtpStorage.java中mStorageId”。

下面,我们看看MtpStorage.h(JNI层)中的成员如下:

复制代码
1 class MtpStorage {
2
3 private:
4 MtpStorageID mStorageID;
5 MtpString mFilePath;
6 MtpString mDescription;
7 uint64_t mMaxCapacity;
8 uint64_t mMaxFileSize;
9 uint64_t mReserveSpace;
10 bool mRemovable;
11
12 …
13 }
复制代码
将MtpStroage.h(JNI层)中的成员和”前面介绍的MtpStroage.java的成员”进行对比,它们非常相似。实际上,MTP就是通过MtpStorage.cpp来获取MtpStorage.java的相关信息的。

9.4 server->addStorage(storage)
MtpServer.cpp中addStorage()的源码如下:

复制代码
1 void MtpServer::addStorage(MtpStorage* storage) {
2 // 获取“信号量”
3 Mutex::Autolock autoLock(mMutex);
4
5 // mStorages是“MtpStorage的Vector容量对象”,相当于“MtpStorage的动态数组”。
6 mStorages.push(storage);
7 // 调用sendStoreAdded()通知PC,让PC通过MTP挂载该存储设备
8 sendStoreAdded(storage->getStorageID());
9 }
复制代码
说明:addStorage()的作用,就是将”输入参数storage”添加到”mStorages动态数组”中进行管理;然后,调用sendStoreAdded()告诉PC挂载该设备。下面看看sendStoreAdded()是如何处理的。

9.5 sendStoreAdded()
MtpServer.cpp中sendStoreAdded()的源码如下:

1 void MtpServer::sendStoreAdded(MtpStorageID id) {
2 sendEvent(MTP_EVENT_STORE_ADDED, id);
3 }
说明:

MTP_EVENT_STORE_ADDED是”MtpEventCode类型”的数组中的成员,而MtpEventCode是uint16_t类型。所以,MTP_EVENT_STORE_ADDED是一个”16位的无符号整型数”,它代表的是一个MTP事件指令ID。

9.6 sendEvent()
MtpServer.cpp中sendEvent()的源码如下:

复制代码
1 void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
2 if (mSessionOpen) {
3 mEvent.setEventCode(code);
4 mEvent.setTransactionID(mRequest.getTransactionID());
5 mEvent.setParameter(1, param1);
6 int ret = mEvent.write(mFD);
7 ALOGV(“mEvent.write returned %d\n”, ret);
8 }
9 }
复制代码
说明:

(01) mSessionOpen是”MTP会话是否打开的标记”。当PC和Android设备连上之后,PC会发送”打开会话指令”给Android设备从而开打会话;到PC和Android设备断开时,会话才结束。在打开会话时,会设置mSessionOpen为true。这也意味着,此时的mSessionOpen已经为true。

(02) mEvent是MtpEventPacket对象。而MtpEventPacket继承于MtpPacket,MtpEventPacket声明如下:

class MtpEventPacket : public MtpPacket {}

9.7 mEvent.write()
sendEvent()在执行mEvent.write()之前,会初始化mEvent对象。

9.7.1 setEventCode()

setEventCode()在MtpEventPacket.h中实现,源码如下:

inline void setEventCode(MtpEventCode code) { return setContainerCode(code); }

setContainerCode()在MtpPacket.cpp中实现,源码如下:

void MtpPacket::setContainerCode(uint16_t code) {
putUInt16(MTP_CONTAINER_CODE_OFFSET, code);
}

putUInt16()在MtpPacket.cpp中实现,源码如下:

void MtpPacket::putUInt16(int offset, uint16_t value) {
mBuffer[offset++] = (uint8_t)(value & 0xFF);
mBuffer[offset++] = (uint8_t)((value >> 8) & 0xFF);
}
说明:

putUInt16()是”将16位的无符号整型数 写入 到缓冲中”。其中,mBuffer是”数据缓冲”。

综上所述,setEventCode(code)的作用就是将”消息的编码写入到缓冲”,等其他数据写入缓冲之后,再一起发送给PC。对于”添加存储设备而言”,Android设备发给PC的消息的编码就是MTP_EVENT_STORE_ADDED。

9.7.2 setTransactionID()

setTransactionID()在MtpPacket.cpp中实现,源码如下:

void MtpPacket::setTransactionID(MtpTransactionID id) {
putUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET, id);
}

putUInt32()在MtpPacket.cpp中实现,源码如下:

void MtpPacket::putUInt32(int offset, uint32_t value) {
mBuffer[offset++] = (uint8_t)(value & 0xFF);
mBuffer[offset++] = (uint8_t)((value >> 8) & 0xFF);
mBuffer[offset++] = (uint8_t)((value >> 16) & 0xFF);
mBuffer[offset++] = (uint8_t)((value >> 24) & 0xFF);
}
说明:

putUInt32()和putUInt16()作用类似,putUInt32()的作用是”将32位的无符号整型数 写入 到缓冲中”。其中,mBuffer是”数据缓冲”。

综上所述,setTransactionID()就是将”TransactionID写入到缓冲”。

9.7.3 setParameter()

setParameter()在MtpPacket.cpp中实现,源码如下:

复制代码
1 void MtpPacket::setParameter(int index, uint32_t value) {
2 if (index < 1 || index > 5) {
3 ALOGE(“index %d out of range in MtpPacket::setParameter”, index);
4 return;
5 }
6 int offset = MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t);
7 if (mPacketSize < offset + sizeof(uint32_t))
8 mPacketSize = offset + sizeof(uint32_t);
9 putUInt32(offset, value);
10 }
复制代码
说明: setParameter()的作用是”将StorageID写入缓冲”,从而告诉PC操作哪个”存储设备”。

9.7.4 write()

write()在MtpEventPacket.cpp中实现,源码如下:

复制代码
1 int MtpEventPacket::write(int fd) {
2 struct mtp_event event;
3
4 putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
5 putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_EVENT);
6
7 // 将“缓冲”赋值给event.data
8 event.data = mBuffer;
9 event.length = mPacketSize;
10 // 通过“ioctl事件”将该消息发送给驱动,驱动再负责和PC通信。
11 int ret = ::ioctl(fd, MTP_SEND_EVENT, (unsigned long)&event);
12 return (ret < 0 ? ret : 0);
13 }
复制代码
说明:

write()的作用,就是将打包好的消息发送给PC。write()的作用是将消息传递给Kernel,真正传递给PC是在Kernel的驱动中进行处理的。感兴趣的可以看看下面Kernel的write()介绍部分。

下面简单介绍下Kernel中write()是如何实现的。通过前面介绍的MTP架构图,我们知道:

(01) PC和Android设备是通过USB协议来进行MTP通信的。

  注意:MTP协议除了USB之外,还可以通过BT等协议进行通信;这里只对USB进行说明。

(02) PC给Android设备发送消息时,PC会通过USB传输数据给内核。MTP驱动再从USB中读取出”PC传过来的消息”,然后写入到”/dev/mtp_usb”文件节点上。”/dev/mtp_usb”是MTP驱动对应的节点,在Linux中,一切都是文件;用户通过操作”/dev/mtp_usb”节点,就能读取出”PC发过来的消息”。

(03) Android设备给PC发送消息时,会向将信息写入到”/dev/mtp_usb”文件中。MTP驱动从”/dev/mtp_usb”中读取数据之后,再通过USB传给送PC。


关注我的技术公众号,查看更多优质技术文章推送

微信扫一扫下方二维码即可关注:

关注我的技术公众号,查看更多优质技术文章推送

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值