前言
         欢迎大家我分享和推荐好用的代码段~~
声明
         欢迎转载,但请保留文章原始出处:
         CSDN
http://www.csdn.net
         雨季o莫忧离:http://blog.csdn.net/luckkof

正文

 



概要

本文的目的是介绍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!

 

第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所示:

图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。 MtpPacketMtpEventPacket负责对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连接消息,并最终通知UsbManagerUsbManager发出广播,并且广播被MtpReceiver收到;MtpReceiver收到广播后会启动MtpService,同时通知MediaProviderMediaProvider会与MtpService绑定,若Android设备中的文件结构有变化(如"新键文件"),MediaProvider则会通知MtpServiceMtpService启动后会创建MtpDatabase;之后,还会创建MtpServerMtpServer会和MtpDatabase关联。然后,MtpService会遍历本地的存储设备,并建立相应的MtpStorage,并将该MtpStorage添加到MtpDatabaseMtpServer中。最后,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.javapackages/providers/MediaProvider/src/com/android/providers/media/MtpService.javapackages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.javaframeworks/base/media/java/android/mtp/MtpServer.javaframeworks/base/media/java/android/mtp/MtpDatabase.javaframeworks/base/media/java/android/mtp/MtpStorage.javaframeworks/base/media/jni/android_mtp_MtpServer.cppframeworks/base/media/jni/android_mtp_MtpDatabase.cppframeworks/av/media/mtp/MtpServer.hframeworks/av/media/mtp/MtpServer.cppframeworks/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 <receiver android:name=".MtpReceiver">2     <intent-filter>3         <action android:name="android.intent.action.BOOT_COMPLETED" />4     intent-filter>5     <intent-filter>6         <action android:name="android.hardware.usb.action.USB_STATE" />7     intent-filter>8 receiver>

说明

这是采用静态方式注册的广播,经过上面的注册之后,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 exists17                 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         // 启动MtpServer12         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 static2 {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对象database4     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         // 存储设备ID11         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。

 

一. MTP驱动注册

MTP驱动文件是drivers/usb/gadget/f_mtp.c。它通过下面的代码会映射到文件节点"/dev/mtp_usb"中。

 1 static const char mtp_shortname[] = "mtp_usb"; 2  3 static const struct file_operations mtp_fops = { 4     .owner = THIS_MODULE, 5     .read = mtp_read, 6     .write = mtp_write, 7     .unlocked_ioctl = mtp_ioctl, 8     .open = mtp_open, 9     .release = mtp_release,10 }; 11 12 static struct miscdevice mtp_device = {13     .minor = MISC_DYNAMIC_MINOR,14     .name = mtp_shortname,15     .fops = &mtp_fops,16 };  17 18 static int mtp_setup(void)19 {20     ...21 22     ret = misc_register(&mtp_device);23 24     ...25 }

说明

(01) misc_register(&mtp_device)会将MTP注册到虚拟文件系统"/dev"中,而对应的注册的文件节点的名称是"mtp_usb",即完成的节点是"/dev/mtp_usb"。

(02) 用户空间操作节点"/dev/mtp_usb"就是调用MTP驱动中file_operations中的相关函数。

例如,用户空间通过read()去读取"/dev/mtp_usb",实际上调用内核空间的是mtp_read();用户空间通过write()去写"/dev/mtp_usb",实际上调用内核空间的是mtp_write()。

 

二. mtp_read()

该函数在f_mtp.c中实现,源码如下:

 1 static ssize_t mtp_read(struct file *fp, char __user *buf, 2     size_t count, loff_t *pos) 3 { 4     // 从“USB”消息队列中中读取PC发给Android设备的请求,请求消息保存在req中。 5     ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); 6     ... 7  8     // 等待cpu将工作队列(read_wq)上已有的消息处理完毕 9     ret = wait_event_interruptible(dev->read_wq, dev->rx_done);10     ...11 12     // 将“PC的请求数据(req->)”从“内核空间”拷贝到“用户空间”13     if (copy_to_user(buf, req->buf, xfer))14         r = -EFAULT;15 16     ...17 }

说明mtp_read()会通过USB读取"PC发给Android设备的请求",获取到请求后,会通过copy_to_user()会将数据从"内核空间"拷贝到"用户空间",这样用户就能在用户空间读取到该数据。

 

三. mtp_write()

 1 static ssize_t mtp_write(struct file *fp, const char __user *buf, 2     size_t count, loff_t *pos) 3 { 4  5     while (count > 0 || sendZLP) { 6  7         // 将“用户空间”传来的消息(buf)拷贝到“内核空间”的req->buf中。 8         if (xfer && copy_from_user(req->buf, buf, xfer))  9         ...10 11         // 将打包好的消息req放到USB消息队列中。12         ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);13         ...14     }15 16     ...17 }

说明:mtp_write()会将"用户空间"发来的消息拷贝到"内核空间",并将该消息打包;然后,将打包好的消息添加到USB消息队列中。USB驱动负责将消息队列中的消息传递给PC。

 

 

10 mServer.start()

MtpServer实际上是一个Runnable线程接口。在"MtpReceiver的handleUsbState()"中,初始化MtpServer之后,会启动MtpServer。

MtpServer中的run()方法如下:

@Overridepublic void run() {    native_run();    native_cleanup();}

从中,我们发现run()实际上是调用的本地方法native_run()。

 

10.1 native_run()

根据前面的gMethods表格,我们知道native_run()对应JNI层的android_mtp_MtpServer_run()方法,它的源码如下:

 1 static void android_mtp_MtpServer_run(JNIEnv *env, jobject thiz) 2 { 3     // 返回MtpServer对象 4     MtpServer* server = getMtpServer(env, thiz); 5     // 如果server不为NULL,则调用它的run()方法。 6     if (server) 7         server->run(); 8     else 9         ALOGE("server is null in run");10 }

说明

(01) getMtpServer()返回的是MtpServer对象。这个前面已经介绍过!

(02) 调用MtpServer对象的run()方法。

 

10.2 run()

 1 void MtpServer::run() { 2     // 将mFD赋值给fd,fd就是“/dev/mtp_usb”的句柄 3     int fd = mFD; 4  5     while (1) { 6         // 读取“/dev/mtp_usb” 7         int ret = mRequest.read(fd); 8  9         ...10         // 获取“MTP操作码”11         MtpOperationCode operation = mRequest.getOperationCode();12         MtpTransactionID transaction = mRequest.getTransactionID();13 14         ...15 16         // 在handleRequest()中,根据读取的指令作出相应的处理17         if (handleRequest()) {18             ...19         }20     }21 22     ...23 }

说明

run()会不断的从"/dev/mtp_usb"中读取数据。如果是有效的指令,则在handleRequest()作出相应的处理。

(01) mRequest是MtpRequestPacket对象,MtpRequestPacket是解析"PC请求指令"的类。

(02) handleRequest()是具体处理"MTP各个指令的类"。例如,PC获取Android设备的设备信息指令,是在handleRequest()中处理的。

 

 

10.3 while(1){...}

在run()中,会通过while(1)循环不断的"执行read(),从"/dev/mtp_usb中读取数据";然后调用handleRequest()对数据进行处理"。

read()函数最终会调用到Kernel的mtp_read(),mtp_read()已经在前面介绍过了。

handleRequest()则是对读取出来的消息进行处理,它的源码如下:

 1 bool MtpServer::handleRequest() { 2     Mutex::Autolock autoLock(mMutex); 3  4     MtpOperationCode operation = mRequest.getOperationCode(); 5     MtpResponseCode response; 6  7     mResponse.reset(); 8  9     if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {10         mSendObjectHandle = kInvalidObjectHandle;11     }12 13     switch (operation) {14         case MTP_OPERATION_GET_DEVICE_INFO:15             response = doGetDeviceInfo();16             break;17         case MTP_OPERATION_OPEN_SESSION:18             response = doOpenSession();19             break;20         case MTP_OPERATION_CLOSE_SESSION:21             response = doCloseSession();22             break;23         ...24         case MTP_OPERATION_GET_OBJECT:25             response = doGetObject();26             break;27         case MTP_OPERATION_SEND_OBJECT:28             response = doSendObject();29             break;30         ...31     }32 33     if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)34         return false;35     mResponse.setResponseCode(response);36     return true;37 }

 

 

总结:在"PC和Android设备"连接后,MtpReceiver会收到广播。接着MtpReceiver会启动MtpService,MtpService会启动MtpServer(Java层)。MtpServer(Java)层会调用底层的JNI函数。在JNI中,会打开MTP文件节点"/dev/mtp_usb",然后MtpServer(JNI层)会不断的从中读取消息并进行处理。

 

 

第4部分 MTP协议之I->R流程

下面以"PC中打开一个MTP上的文件(读取文件内容)"来对"MTP协议中Initiator到Reponser的流程"进行说明。PC读取文件内容的时序图如图4-01所示:

图4-01

 

1 read()

根据MTP启动流程中分析可知: MTP启动后,MtpServer.cpp中的MtpServer::run()会通过read()不断地从"/dev/mtp_usb"中读取出"PC发来的消息"。

 

2 handleRequest()

read()在读取到PC来的消息之后,会交给MtpServer::handleRequest()进行处理。"PC读取文件内容"的消息的ID是MTP_OPERATION_GET_OBJECT;因此,它会通过doGetObject()进行处理。

 

3. doGetObject()

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

 1 MtpResponseCode MtpServer::doGetObject() { 2     ... 3      4     // 根据handle获取文件的路径(pathBuf)、大小(fileLength)和“格式”。 5     int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format); 6     if (result != MTP_RESPONSE_OK) 7         return result; 8  9     // 将文件路径转换为const char*类型。10     const char* filePath = (const char *)pathBuf;11     // 将文件的信息传递给mfr,mfr是kernel的一个结构体;最终将mfr传递给kernel。12     mtp_file_range  mfr;13     mfr.fd = open(filePath, O_RDONLY);                 // 设置“文件句柄”。14     if (mfr.fd < 0) {15         return MTP_RESPONSE_GENERAL_ERROR;16     }17     mfr.offset = 0;                                    // 设置“文件偏移”18     mfr.length = fileLength;19     mfr.command = mRequest.getOperationCode();         // 设置“command”20     mfr.transaction_id = mRequest.getTransactionID();  // 设置“transaction ID”21 22     // 通过ioctl将文件传给kernel。23     int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);24     // 关闭文件25     close(mfr.fd);26 27     ...28 }

说明

doGetDeviceInfo的流程就是,先通过JNI回调Java中的函数,(根据文件句柄)从MediaProvider数据库中获取文件的路径、大小和格式等信息。接着,将这些信息封装到kernel的"mtp_file_range结构体"中。最后,通过ioctl将"mtp_file_range结构体"传递给kernel。这样,kernel就收到文件的相关信息了,kernel负责将文件信息通过USB协议传递给PC。

 

4 getObjectFilePath()

doGetObject()会调用的getObjectFilePath()。该函数在android_mtp_MtpDatabase.cpp中实现。

 1 MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle, 2                                             MtpString& outFilePath, 3                                             int64_t& outFileLength, 4                                             MtpObjectFormat& outFormat) { 5     ... 6  7     // 调用MtpDatabase.java中的getObjectFilePath()。 8     // 作用是根据文件句柄,获取“文件路径”、“文件大小”、“文件格式” 9     jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath,10                 (jint)handle, mStringBuffer, mLongBuffer);11 12     // 设置“文件路径”13     jchar* str = env->GetCharArrayElements(mStringBuffer, 0);14     outFilePath.setTo(str, strlen16(str));15     env->ReleaseCharArrayElements(mStringBuffer, str, 0);16 17     // 设置“文件大小”和“文件格式”18     jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);19     outFileLength = longValues[0];20     outFormat = longValues[1];21     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);22 23 24     ...25 }

说明

MyMtpDatabase::getObjectFilePath()实际上通过MtpDatabase.java中的getObjectFilePath()来获取文件的相关信息的。

 

5 getObjectFilePath()

MtpDatabase.java中getObjectFilePath()的源码如下:

 1 private int getObjectFilePath(int handle, char[] outFilePath, long[] outFileLengthFormat) { 2     ... 3  4     // 在MediaProvider中查找文件对应的路径。 5     // mObjectsUri是根据文件handle获取到的URI。 6     c = mMediaProvider.query(mPackageName, mObjectsUri, PATH_FORMAT_PROJECTION, 7                     ID_WHERE, new String[] {  Integer.toString(handle) }, null, null); 8     if (c != null && c.moveToNext()) { 9         // 获取“文件路径”10         String path = c.getString(1);11         path.getChars(0, path.length(), outFilePath, 0);12         outFilePath[path.length()] = 0;13         // 获取“文件大小”14         outFileLengthFormat[0] = new File(path).length();15         // 获取“文件格式”16         outFileLengthFormat[1] = c.getLong(2);17         return MtpConstants.RESPONSE_OK;18     } else {19         return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;20     }21 22     ...23 }

说明:getObjectFilePath()会从MediaProvider的数据库中查找相应的文件,从而获取文件信息。然后,将文件的信息回传给JNI。

 

6 ioctl

MtpServer.cpp中doGetObject()中获取到文件信息之后,会通过ioctl将MTP_SEND_FILE_WITH_HEADER消息传递给Kernel。

根据前面介绍的Kernel内容克制,ioctl在file_operations中注册,ioctl实际上会执行mtp_ioctl()函数。它的源码如下:

 1 static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) 2 { 3     ... 4  5     switch (code) { 6     case MTP_SEND_FILE: 7     case MTP_RECEIVE_FILE: 8     case MTP_SEND_FILE_WITH_HEADER: 9     {10         struct mtp_file_range    mfr;11         struct work_struct *work;12 13         // 将“用户空间”传来的值(value)拷贝到“内核空间”的mfr中。14         if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) {15             ret = -EFAULT;16             goto fail;17         }18         // 获取文件句柄19         filp = fget(mfr.fd);20         if (!filp) {21             ret = -EBADF;22             goto fail;23         }24 25         // 将“文件相关的信息”赋值给dev。26         dev->xfer_file = filp;27         dev->xfer_file_offset = mfr.offset;28         dev->xfer_file_length = mfr.length;29         smp_wmb();30 31         if (code == MTP_SEND_FILE_WITH_HEADER) {32             // 设置work的值33             work = &dev->send_file_work;34             dev->xfer_send_header = 1;35             dev->xfer_command = mfr.command;36             dev->xfer_transaction_id = mfr.transaction_id;37         } 38         ...39 40         // 将work添加到工作队列(dev->wq)中。41         queue_work(dev->wq, work);42         // 对工作队列执行flush操作。43         flush_workqueue(dev->wq);44 45         break;46     }47 48     ...49 }

说明:mtp_ioctl()在收到MTP_SEND_FILE_WITH_HEADER消息之后,会获取文件相关信息。然后将"work"添加到工作队列dev->wq中进行调度。work对应的函数指针是send_file_work()函数;这就意味着,工作队列会制定send_file_work()函数。

 

send_file_work()的源码如下:

 1 static void send_file_work(struct work_struct *data) 2 { 3     // 获取dev结构体 4     struct mtp_dev *dev = container_of(data, struct mtp_dev, 5                         send_file_work); 6     ... 7  8     // 读取文件消息 9     smp_rmb();10     filp = dev->xfer_file;11     offset = dev->xfer_file_offset;12     count = dev->xfer_file_length;13 14 15     while (count > 0 || sendZLP) {16 17         ...18 19         // 从文件中读取数据到内存中。20         ret = vfs_read(filp, req->buf + hdr_size, xfer - hdr_size,21                                 &offset);22 23         ...24 25         // 将req添加到usb终端的队列中26         ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);27 28         ...29     }30 31     ...32 }

说明:send_file_work()的作用就是不断地将文件中的数据读取到内存中,并封装到USB请求结构体req中。然后,再将数据req传递到USB工作队列,USB赋值将文件内容传递给PC。

 

至此,PC读取文件内容的流程分析完毕!

 

第5部分 MTP协议之R->I流程

下面以"Android设备中将一个文件拷贝到其他目录"来对"MTP协议中Reponser到Initiator的流程"进行说明。对应的时序图如图5-01所示:

 

图5-01

 

1 MediaProvider.java中sendObjectAdded()

在Android设备上将一个文件拷贝到其他目录。文件浏览器中会发出一个Intent事件,通知MediaProvider更新数据库。MediaProvider更新了数据库之后,会通知MTP进行同步处理。MediaProvider通知MTP的源码如下:

 1 private void sendObjectAdded(long objectHandle) { 2     synchronized (mMtpServiceConnection) { 3         if (mMtpService != null) { 4             try { 5                 mMtpService.sendObjectAdded((int)objectHandle); 6             } catch (RemoteException e) { 7                 Log.e(TAG, "RemoteException in sendObjectAdded", e); 8                 mMtpService = null; 9             }10         }11     }12 }

说明:该函数会通知MtpService,Android设备中新建了个文件。

 

2 MtpService.java中sendObjectAdded()

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

1 MtpService.java中sendObjectAdded()的源码如下:2 public void sendObjectAdded(int objectHandle) {3     synchronized (mBinder) {4         if (mServer != null) {5             mServer.sendObjectAdded(objectHandle);6         }7     }8 }

说明:该函数会发送消息给mServer,而mServer是MtpServer对象。

 

3 MtpServer.java中的sendObjectAdded

1 public void sendObjectAdded(int handle) {2     native_send_object_added(handle);3 }

说明:该函数会调研JNI本地方法。

 

native_send_object_added()

native_send_object_added()在android_mtp_MtpServer.cpp中实现。根据前面介绍相关内容可知,native_send_object_added()和android_mtp_MtpServer_send_object_added()对应。后者的源码如下:

 1 static void android_mtp_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle) 2 { 3     Mutex::Autolock autoLock(sMutex); 4  5     // 获取MtpServer,然后调用MtpServer的sendObjectAdded()方法。 6     MtpServer* server = getMtpServer(env, thiz); 7     if (server) 8         server->sendObjectAdded(handle); 9     else10         ALOGE("server is null in send_object_added");11 }

说明:该函数会将消息发送给MtpServer。

 

5 MtpServer.cpp中的sendObjectAdded

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

1 void MtpServer::sendObjectAdded(MtpObjectHandle handle) {2     sendEvent(MTP_EVENT_OBJECT_ADDED, handle);3 }

说明:sendEvent()的作用,我们前面已经介绍过了。它在此处的目的,就是通过ioctl将消息发送给Kernel。

 

6 Kernel中的ioctl

在Kernel中,ioctl消息会调用mtp_ioctl()进行处理。它的源码如下:

 1 static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) 2 { 3     ... 4  5     case MTP_SEND_EVENT: 6     { 7         struct mtp_event    event; 8         // 将“用户空间”传来的值(value)拷贝到“内核空间”的event中。 9         if (copy_from_user(&event, (void __user *)value, sizeof(event)))10             ret = -EFAULT;11         else12             ret = mtp_send_event(dev, &event);13 14         ...15     }16     17     ...18 }

说明:对于MTP_SEND_EVENT消息,mtp_ioctl会先将"用户空间"传递来的消息拷贝到"内核空间";然后,调用mtp_send_event()进行处理。

 

7 Kernel中的mtp_send_event()

 1 static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) 2 { 3     ... 4  5     // 将“用户空间”传来的“消息的内容(event->data)”拷贝到“内核空间”中 6     if (copy_from_user(req->buf, (void __user *)event->data, length))  7     ... 8  9     // 将req请求添加到USB队列中。10     ret = usb_ep_queue(dev->ep_intr, req, GFP_KERNEL);11 12     ...13 }

说明:mtp_send_event()会先将"用户空间"传递来的消息的具体内容拷贝到"内核空间",并将该消息封装在一个USB请求对象req中;然后,将USB请求添加到USB队列中。USB驱动负责将该数据通过USB线发送给PC。

 

至此,Android设备中将一个文件拷贝到其他目录的流程分析完毕!


概要

本文的目的是介绍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所示:

图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。 MtpPacketMtpEventPacket负责对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连接消息,并最终通知UsbManagerUsbManager发出广播,并且广播被MtpReceiver收到;MtpReceiver收到广播后会启动MtpService,同时通知MediaProviderMediaProvider会与MtpService绑定,若Android设备中的文件结构有变化(如"新键文件"),MediaProvider则会通知MtpServiceMtpService启动后会创建MtpDatabase;之后,还会创建MtpServerMtpServer会和MtpDatabase关联。然后,MtpService会遍历本地的存储设备,并建立相应的MtpStorage,并将该MtpStorage添加到MtpDatabaseMtpServer中。最后,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.javapackages/providers/MediaProvider/src/com/android/providers/media/MtpService.javapackages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.javaframeworks/base/media/java/android/mtp/MtpServer.javaframeworks/base/media/java/android/mtp/MtpDatabase.javaframeworks/base/media/java/android/mtp/MtpStorage.javaframeworks/base/media/jni/android_mtp_MtpServer.cppframeworks/base/media/jni/android_mtp_MtpDatabase.cppframeworks/av/media/mtp/MtpServer.hframeworks/av/media/mtp/MtpServer.cppframeworks/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 <receiver android:name=".MtpReceiver">2     <intent-filter>3         <action android:name="android.intent.action.BOOT_COMPLETED" />4     intent-filter>5     <intent-filter>6         <action android:name="android.hardware.usb.action.USB_STATE" />7     intent-filter>8 receiver>

说明

这是采用静态方式注册的广播,经过上面的注册之后,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 exists17                 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         // 启动MtpServer12         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 static2 {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对象database4     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         // 存储设备ID11         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。

 

一. MTP驱动注册

MTP驱动文件是drivers/usb/gadget/f_mtp.c。它通过下面的代码会映射到文件节点"/dev/mtp_usb"中。

 1 static const char mtp_shortname[] = "mtp_usb"; 2  3 static const struct file_operations mtp_fops = { 4     .owner = THIS_MODULE, 5     .read = mtp_read, 6     .write = mtp_write, 7     .unlocked_ioctl = mtp_ioctl, 8     .open = mtp_open, 9     .release = mtp_release,10 }; 11 12 static struct miscdevice mtp_device = {13     .minor = MISC_DYNAMIC_MINOR,14     .name = mtp_shortname,15     .fops = &mtp_fops,16 };  17 18 static int mtp_setup(void)19 {20     ...21 22     ret = misc_register(&mtp_device);23 24     ...25 }

说明

(01) misc_register(&mtp_device)会将MTP注册到虚拟文件系统"/dev"中,而对应的注册的文件节点的名称是"mtp_usb",即完成的节点是"/dev/mtp_usb"。

(02) 用户空间操作节点"/dev/mtp_usb"就是调用MTP驱动中file_operations中的相关函数。

例如,用户空间通过read()去读取"/dev/mtp_usb",实际上调用内核空间的是mtp_read();用户空间通过write()去写"/dev/mtp_usb",实际上调用内核空间的是mtp_write()。

 

二. mtp_read()

该函数在f_mtp.c中实现,源码如下:

 1 static ssize_t mtp_read(struct file *fp, char __user *buf, 2     size_t count, loff_t *pos) 3 { 4     // 从“USB”消息队列中中读取PC发给Android设备的请求,请求消息保存在req中。 5     ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); 6     ... 7  8     // 等待cpu将工作队列(read_wq)上已有的消息处理完毕 9     ret = wait_event_interruptible(dev->read_wq, dev->rx_done);10     ...11 12     // 将“PC的请求数据(req->)”从“内核空间”拷贝到“用户空间”13     if (copy_to_user(buf, req->buf, xfer))14         r = -EFAULT;15 16     ...17 }

说明mtp_read()会通过USB读取"PC发给Android设备的请求",获取到请求后,会通过copy_to_user()会将数据从"内核空间"拷贝到"用户空间",这样用户就能在用户空间读取到该数据。

 

三. mtp_write()

 1 static ssize_t mtp_write(struct file *fp, const char __user *buf, 2     size_t count, loff_t *pos) 3 { 4  5     while (count > 0 || sendZLP) { 6  7         // 将“用户空间”传来的消息(buf)拷贝到“内核空间”的req->buf中。 8         if (xfer && copy_from_user(req->buf, buf, xfer))  9         ...10 11         // 将打包好的消息req放到USB消息队列中。12         ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);13         ...14     }15 16     ...17 }

说明:mtp_write()会将"用户空间"发来的消息拷贝到"内核空间",并将该消息打包;然后,将打包好的消息添加到USB消息队列中。USB驱动负责将消息队列中的消息传递给PC。

 

 

10 mServer.start()

MtpServer实际上是一个Runnable线程接口。在"MtpReceiver的handleUsbState()"中,初始化MtpServer之后,会启动MtpServer。

MtpServer中的run()方法如下:

@Overridepublic void run() {    native_run();    native_cleanup();}

从中,我们发现run()实际上是调用的本地方法native_run()。

 

10.1 native_run()

根据前面的gMethods表格,我们知道native_run()对应JNI层的android_mtp_MtpServer_run()方法,它的源码如下:

 1 static void android_mtp_MtpServer_run(JNIEnv *env, jobject thiz) 2 { 3     // 返回MtpServer对象 4     MtpServer* server = getMtpServer(env, thiz); 5     // 如果server不为NULL,则调用它的run()方法。 6     if (server) 7         server->run(); 8     else 9         ALOGE("server is null in run");10 }

说明

(01) getMtpServer()返回的是MtpServer对象。这个前面已经介绍过!

(02) 调用MtpServer对象的run()方法。

 

10.2 run()

 1 void MtpServer::run() { 2     // 将mFD赋值给fd,fd就是“/dev/mtp_usb”的句柄 3     int fd = mFD; 4  5     while (1) { 6         // 读取“/dev/mtp_usb” 7         int ret = mRequest.read(fd); 8  9         ...10         // 获取“MTP操作码”11         MtpOperationCode operation = mRequest.getOperationCode();12         MtpTransactionID transaction = mRequest.getTransactionID();13 14         ...15 16         // 在handleRequest()中,根据读取的指令作出相应的处理17         if (handleRequest()) {18             ...19         }20     }21 22     ...23 }

说明

run()会不断的从"/dev/mtp_usb"中读取数据。如果是有效的指令,则在handleRequest()作出相应的处理。

(01) mRequest是MtpRequestPacket对象,MtpRequestPacket是解析"PC请求指令"的类。

(02) handleRequest()是具体处理"MTP各个指令的类"。例如,PC获取Android设备的设备信息指令,是在handleRequest()中处理的。

 

 

10.3 while(1){...}

在run()中,会通过while(1)循环不断的"执行read(),从"/dev/mtp_usb中读取数据";然后调用handleRequest()对数据进行处理"。

read()函数最终会调用到Kernel的mtp_read(),mtp_read()已经在前面介绍过了。

handleRequest()则是对读取出来的消息进行处理,它的源码如下:

 1 bool MtpServer::handleRequest() { 2     Mutex::Autolock autoLock(mMutex); 3  4     MtpOperationCode operation = mRequest.getOperationCode(); 5     MtpResponseCode response; 6  7     mResponse.reset(); 8  9     if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {10         mSendObjectHandle = kInvalidObjectHandle;11     }12 13     switch (operation) {14         case MTP_OPERATION_GET_DEVICE_INFO:15             response = doGetDeviceInfo();16             break;17         case MTP_OPERATION_OPEN_SESSION:18             response = doOpenSession();19             break;20         case MTP_OPERATION_CLOSE_SESSION:21             response = doCloseSession();22             break;23         ...24         case MTP_OPERATION_GET_OBJECT:25             response = doGetObject();26             break;27         case MTP_OPERATION_SEND_OBJECT:28             response = doSendObject();29             break;30         ...31     }32 33     if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)34         return false;35     mResponse.setResponseCode(response);36     return true;37 }

 

 

总结:在"PC和Android设备"连接后,MtpReceiver会收到广播。接着MtpReceiver会启动MtpService,MtpService会启动MtpServer(Java层)。MtpServer(Java)层会调用底层的JNI函数。在JNI中,会打开MTP文件节点"/dev/mtp_usb",然后MtpServer(JNI层)会不断的从中读取消息并进行处理。

 

 

第4部分 MTP协议之I->R流程

下面以"PC中打开一个MTP上的文件(读取文件内容)"来对"MTP协议中Initiator到Reponser的流程"进行说明。PC读取文件内容的时序图如图4-01所示:

图4-01

 

1 read()

根据MTP启动流程中分析可知: MTP启动后,MtpServer.cpp中的MtpServer::run()会通过read()不断地从"/dev/mtp_usb"中读取出"PC发来的消息"。

 

2 handleRequest()

read()在读取到PC来的消息之后,会交给MtpServer::handleRequest()进行处理。"PC读取文件内容"的消息的ID是MTP_OPERATION_GET_OBJECT;因此,它会通过doGetObject()进行处理。

 

3. doGetObject()

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

 1 MtpResponseCode MtpServer::doGetObject() { 2     ... 3      4     // 根据handle获取文件的路径(pathBuf)、大小(fileLength)和“格式”。 5     int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format); 6     if (result != MTP_RESPONSE_OK) 7         return result; 8  9     // 将文件路径转换为const char*类型。10     const char* filePath = (const char *)pathBuf;11     // 将文件的信息传递给mfr,mfr是kernel的一个结构体;最终将mfr传递给kernel。12     mtp_file_range  mfr;13     mfr.fd = open(filePath, O_RDONLY);                 // 设置“文件句柄”。14     if (mfr.fd < 0) {15         return MTP_RESPONSE_GENERAL_ERROR;16     }17     mfr.offset = 0;                                    // 设置“文件偏移”18     mfr.length = fileLength;19     mfr.command = mRequest.getOperationCode();         // 设置“command”20     mfr.transaction_id = mRequest.getTransactionID();  // 设置“transaction ID”21 22     // 通过ioctl将文件传给kernel。23     int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);24     // 关闭文件25     close(mfr.fd);26 27     ...28 }

说明

doGetDeviceInfo的流程就是,先通过JNI回调Java中的函数,(根据文件句柄)从MediaProvider数据库中获取文件的路径、大小和格式等信息。接着,将这些信息封装到kernel的"mtp_file_range结构体"中。最后,通过ioctl将"mtp_file_range结构体"传递给kernel。这样,kernel就收到文件的相关信息了,kernel负责将文件信息通过USB协议传递给PC。

 

4 getObjectFilePath()

doGetObject()会调用的getObjectFilePath()。该函数在android_mtp_MtpDatabase.cpp中实现。

 1 MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle, 2                                             MtpString& outFilePath, 3                                             int64_t& outFileLength, 4                                             MtpObjectFormat& outFormat) { 5     ... 6  7     // 调用MtpDatabase.java中的getObjectFilePath()。 8     // 作用是根据文件句柄,获取“文件路径”、“文件大小”、“文件格式” 9     jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath,10                 (jint)handle, mStringBuffer, mLongBuffer);11 12     // 设置“文件路径”13     jchar* str = env->GetCharArrayElements(mStringBuffer, 0);14     outFilePath.setTo(str, strlen16(str));15     env->ReleaseCharArrayElements(mStringBuffer, str, 0);16 17     // 设置“文件大小”和“文件格式”18     jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);19     outFileLength = longValues[0];20     outFormat = longValues[1];21     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);22 23 24     ...25 }

说明

MyMtpDatabase::getObjectFilePath()实际上通过MtpDatabase.java中的getObjectFilePath()来获取文件的相关信息的。

 

5 getObjectFilePath()

MtpDatabase.java中getObjectFilePath()的源码如下:

 1 private int getObjectFilePath(int handle, char[] outFilePath, long[] outFileLengthFormat) { 2     ... 3  4     // 在MediaProvider中查找文件对应的路径。 5     // mObjectsUri是根据文件handle获取到的URI。 6     c = mMediaProvider.query(mPackageName, mObjectsUri, PATH_FORMAT_PROJECTION, 7                     ID_WHERE, new String[] {  Integer.toString(handle) }, null, null); 8     if (c != null && c.moveToNext()) { 9         // 获取“文件路径”10         String path = c.getString(1);11         path.getChars(0, path.length(), outFilePath, 0);12         outFilePath[path.length()] = 0;13         // 获取“文件大小”14         outFileLengthFormat[0] = new File(path).length();15         // 获取“文件格式”16         outFileLengthFormat[1] = c.getLong(2);17         return MtpConstants.RESPONSE_OK;18     } else {19         return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;20     }21 22     ...23 }

说明:getObjectFilePath()会从MediaProvider的数据库中查找相应的文件,从而获取文件信息。然后,将文件的信息回传给JNI。

 

6 ioctl

MtpServer.cpp中doGetObject()中获取到文件信息之后,会通过ioctl将MTP_SEND_FILE_WITH_HEADER消息传递给Kernel。

根据前面介绍的Kernel内容克制,ioctl在file_operations中注册,ioctl实际上会执行mtp_ioctl()函数。它的源码如下:

 1 static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) 2 { 3     ... 4  5     switch (code) { 6     case MTP_SEND_FILE: 7     case MTP_RECEIVE_FILE: 8     case MTP_SEND_FILE_WITH_HEADER: 9     {10         struct mtp_file_range    mfr;11         struct work_struct *work;12 13         // 将“用户空间”传来的值(value)拷贝到“内核空间”的mfr中。14         if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) {15             ret = -EFAULT;16             goto fail;17         }18         // 获取文件句柄19         filp = fget(mfr.fd);20         if (!filp) {21             ret = -EBADF;22             goto fail;23         }24 25         // 将“文件相关的信息”赋值给dev。26         dev->xfer_file = filp;27         dev->xfer_file_offset = mfr.offset;28         dev->xfer_file_length = mfr.length;29         smp_wmb();30 31         if (code == MTP_SEND_FILE_WITH_HEADER) {32             // 设置work的值33             work = &dev->send_file_work;34             dev->xfer_send_header = 1;35             dev->xfer_command = mfr.command;36             dev->xfer_transaction_id = mfr.transaction_id;37         } 38         ...39 40         // 将work添加到工作队列(dev->wq)中。41         queue_work(dev->wq, work);42         // 对工作队列执行flush操作。43         flush_workqueue(dev->wq);44 45         break;46     }47 48     ...49 }

说明:mtp_ioctl()在收到MTP_SEND_FILE_WITH_HEADER消息之后,会获取文件相关信息。然后将"work"添加到工作队列dev->wq中进行调度。work对应的函数指针是send_file_work()函数;这就意味着,工作队列会制定send_file_work()函数。

 

send_file_work()的源码如下:

 1 static void send_file_work(struct work_struct *data) 2 { 3     // 获取dev结构体 4     struct mtp_dev *dev = container_of(data, struct mtp_dev, 5                         send_file_work); 6     ... 7  8     // 读取文件消息 9     smp_rmb();10     filp = dev->xfer_file;11     offset = dev->xfer_file_offset;12     count = dev->xfer_file_length;13 14 15     while (count > 0 || sendZLP) {16 17         ...18 19         // 从文件中读取数据到内存中。20         ret = vfs_read(filp, req->buf + hdr_size, xfer - hdr_size,21                                 &offset);22 23         ...24 25         // 将req添加到usb终端的队列中26         ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);27 28         ...29     }30 31     ...32 }

说明:send_file_work()的作用就是不断地将文件中的数据读取到内存中,并封装到USB请求结构体req中。然后,再将数据req传递到USB工作队列,USB赋值将文件内容传递给PC。

 

至此,PC读取文件内容的流程分析完毕!

 

第5部分 MTP协议之R->I流程

下面以"Android设备中将一个文件拷贝到其他目录"来对"MTP协议中Reponser到Initiator的流程"进行说明。对应的时序图如图5-01所示:

 

图5-01

 

1 MediaProvider.java中sendObjectAdded()

在Android设备上将一个文件拷贝到其他目录。文件浏览器中会发出一个Intent事件,通知MediaProvider更新数据库。MediaProvider更新了数据库之后,会通知MTP进行同步处理。MediaProvider通知MTP的源码如下:

 1 private void sendObjectAdded(long objectHandle) { 2     synchronized (mMtpServiceConnection) { 3         if (mMtpService != null) { 4             try { 5                 mMtpService.sendObjectAdded((int)objectHandle); 6             } catch (RemoteException e) { 7                 Log.e(TAG, "RemoteException in sendObjectAdded", e); 8                 mMtpService = null; 9             }10         }11     }12 }

说明:该函数会通知MtpService,Android设备中新建了个文件。

 

2 MtpService.java中sendObjectAdded()

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

1 MtpService.java中sendObjectAdded()的源码如下:2 public void sendObjectAdded(int objectHandle) {3     synchronized (mBinder) {4         if (mServer != null) {5             mServer.sendObjectAdded(objectHandle);6         }7     }8 }

说明:该函数会发送消息给mServer,而mServer是MtpServer对象。

 

3 MtpServer.java中的sendObjectAdded

1 public void sendObjectAdded(int handle) {2     native_send_object_added(handle);3 }

说明:该函数会调研JNI本地方法。

 

native_send_object_added()

native_send_object_added()在android_mtp_MtpServer.cpp中实现。根据前面介绍相关内容可知,native_send_object_added()和android_mtp_MtpServer_send_object_added()对应。后者的源码如下:

 1 static void android_mtp_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle) 2 { 3     Mutex::Autolock autoLock(sMutex); 4  5     // 获取MtpServer,然后调用MtpServer的sendObjectAdded()方法。 6     MtpServer* server = getMtpServer(env, thiz); 7     if (server) 8         server->sendObjectAdded(handle); 9     else10         ALOGE("server is null in send_object_added");11 }

说明:该函数会将消息发送给MtpServer。

 

5 MtpServer.cpp中的sendObjectAdded

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

1 void MtpServer::sendObjectAdded(MtpObjectHandle handle) {2     sendEvent(MTP_EVENT_OBJECT_ADDED, handle);3 }

说明:sendEvent()的作用,我们前面已经介绍过了。它在此处的目的,就是通过ioctl将消息发送给Kernel。

 

6 Kernel中的ioctl

在Kernel中,ioctl消息会调用mtp_ioctl()进行处理。它的源码如下:

 1 static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) 2 { 3     ... 4  5     case MTP_SEND_EVENT: 6     { 7         struct mtp_event    event; 8         // 将“用户空间”传来的值(value)拷贝到“内核空间”的event中。 9         if (copy_from_user(&event, (void __user *)value, sizeof(event)))10             ret = -EFAULT;11         else12             ret = mtp_send_event(dev, &event);13 14         ...15     }16     17     ...18 }

说明:对于MTP_SEND_EVENT消息,mtp_ioctl会先将"用户空间"传递来的消息拷贝到"内核空间";然后,调用mtp_send_event()进行处理。

 

7 Kernel中的mtp_send_event()

 1 static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) 2 { 3     ... 4  5     // 将“用户空间”传来的“消息的内容(event->data)”拷贝到“内核空间”中 6     if (copy_from_user(req->buf, (void __user *)event->data, length))  7     ... 8  9     // 将req请求添加到USB队列中。10     ret = usb_ep_queue(dev->ep_intr, req, GFP_KERNEL);11 12     ...13 }

说明:mtp_send_event()会先将"用户空间"传递来的消息的具体内容拷贝到"内核空间",并将该消息封装在一个USB请求对象req中;然后,将USB请求添加到USB队列中。USB驱动负责将该数据通过USB线发送给PC。

 

至此,Android设备中将一个文件拷贝到其他目录的流程分析完毕!

更多相关文章

  1. 一款常用的 Squid 日志分析工具
  2. GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
  3. RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
  4. Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
  5. Android艺术探索读书笔记 -IPC机制
  6. Android性能测试工具:Emmagee
  7. Android中分页滑动实现总结
  8. 【Android】8.4 让主题自适应不同的Android版本
  9. Android软件包静默安装小应用 - 附源码

随机推荐

  1. 弹窗样式dialog
  2. :Android实现程序前后台切换效果
  3. Android TextView的字符过滤功能
  4. 【android学习笔记】收藏的网上查找的and
  5. Android TV 焦点控制逻辑
  6. java
  7. android sdk 文件目录含义介绍
  8. __builtin_clz(); __builtin_ctz(); __bu
  9. android 布局边框阴影1dp
  10. 【源码分享下载】每日更新之Android快速