2014-10-09 17:50:44 kv110 阅读数 13066

前面转发了篇博客介绍了MTP, 偏重于上层,已经很清楚了。这篇侧重于底层,按照一定的流程讲。

 

1. 代码位置

packages/providers/MediaProvider/src/com/android/providers/media/MtpReceiver.java
packages/providers/MediaProvider/src/com/android/providers/media/MtpService.java
packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
frameworks/base/media/java/android/mtp/MtpServer.java
frameworks/base/media/java/android/mtp/MtpDatabase.java
frameworks/base/media/java/android/mtp/MtpStorage.java
frameworks/base/media/java/android/mtp/MtpPropertyGroup.java
frameworks/base/media/java/android/mtp/MtpPropertyList.java
frameworks/base/media/java/android/mtp/MtpConstants.java
frameworks/base/media/jni/android_mtp_MtpServer.cpp
frameworks/base/media/jni/android_mtp_MtpDatabase.cpp
frameworks/av/media/mtp/MtpServer.h
frameworks/av/media/mtp/MtpServer.cpp
frameworks/av/media/mtp/MtpDatabase.h

frameworks/base/services/java/com/android/server/usb/UsbDeviceManager.java (UsbManager, UsbService)

kernel/drivers/usb/* (主要是android.c, f_mtp.c)

 

2. 在Settings设置mtp后,代码执行

Usbanager.SetCurrentFunction()

    ->UsbService.SetCurrentFunction()

        ->UsbDeviceManager.setCurrentFunctions()

             -> mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS)

                   ->setEnabledFunctions(functions, makeDefault);

                       ->SystemProperties.set("persist.sys.usb.config", functions);

此时会调用到init.usb.rc执行相应的代码

如果直接开机,开机时会在设置persist.sys.usb.config/sys.usb.config时执行init.usb.rc中代码(没有经过framework及以上代码)。

write /sys/class/android_usb/android0/enable 0/1

以上代码对应执行enable_store (android.c)

enable_store
1)enabled
  f->enable(f_holder->f);
     adb_android_function_enable
        android_disable
           usb_ep_dequeue
           usb_remove_config
  android_enable(dev);
    usb_add_config
    usb_gadget_connect
2)disabled
  android_disable(dev);
     usb_ep_dequeue
     usb_remove_config
  f->disable(f_holder->f)
     adb_android_function_disable
  
usb_add_config(android_bind_config)   composite.c
   bind()
     android_bind_config()
         android_bind_enabled_functions()
            f->bind_config()
               mtp_function_bind_config(mtp)
               adb_function_bind_config(adb)
                  -> usb_add_function()
            
            
usb_remove_config()          
   reset_config(cdev);
   unbind_config(cdev, config);
       f->unbind(config, f);
          ****_function_unbind_config()
      config->unbind(config);

 

write /sys/class/android_usb/android0/functions mtp

以上代码对应执行functions_store (android.c)

functions_store()
 -> android_enable_function()
         -> list_add_tail  

 

3. 如果开机后,连上USB线,首先执行chargr检测,然后是枚举过程 (以高通某芯片为例),这小节讲充电器检测

 kernel\drivers\power\qpnp-charger.c

     rc = devm_request_irq(chip->dev, chip->usbin_valid.irq,
    qpnp_chg_usb_usbin_valid_irq_handler,
    IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
     "usbin-valid", chip);

qpnp_chg_usb_usbin_valid_irq_handler()中断处理函数,检测USB插入拔出

在连USB开机的情况下,这个处理函数会在qpnp_charger_probe()调用。

首先是电源恢复工作,

power_supply_set_present()

    ->msm_otg_set_vbus_state(POWER_SUPPLY_PROP_PRESENT)

        ->msm_otg_sm_work()

             ->pm_runtime_resume()

                 ->msm_otg_runtime_resume()

                     ->msm_otg_resume()

此时会看到log   "USB exited from low power mode"

 

开始充电器检测工作

主要函数式msm_chg_detect_work() in msm_otg.c

通常USB线的检测状态变化

USB_CHG_STATE_UNDEFINED->USB_CHG_STATE_WAIT_FOR_DCD(循环几次)->USB_CHG_STATE_DCD_DONE

USB线的检测结果是USB_SDP_CHARGER,

此时会设置otg->phy->state=b_peripheral, 并设置current=100mA

 

4. 枚举过程

4.1 中断

ci13xxx_msm.c
   ret = request_irq(wake_irq, ci13xxx_msm_resume_irq,
                IRQF_TRIGGER_RISING | IRQF_ONESHOT, "usb resume", NULL);
ret = request_irq(_udc_ctxt.irq, msm_udc_irq, IRQF_SHARED, pdev->name, pdev);

msm_udc_irq()直接调用udc_irq(),处理PM and tr complete
isr_reset_handler/isr_suspend_handler/isr_resume_handler/isr_tr_complete_handler

 

msm_otg.c
Vbus检测中断
 ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED,
     "msm_otg", motg);
     
msm_otg_irq()->queue_work(system_nrt_wq, &motg->sm_work);->msm_otg_sm_work()

 

插OTG 线后中断

msm_pmic_id_irq(), ID pin 

 

4.2 调用过程

ci13xxx_udc.c

isr_reset_handler/isr_suspend_handler/isr_resume_handler/isr_tr_complete_handler

 

msm_otg_sm_work() in msm_otg.c

  msm_chg_detect_work(b_idle b_sess_vld USB_SDP_CHARGER)  

    msm_otg_start_peripheral(otg, 1);   "gadget on"

      setup_gpio (.setup_gpio = isp1763_setup_gpio)

      usb_gadget_vbus_connect

       ci13xxx_vbus_session  "vbus online"    ci13xxx_udc.c

         hw_device_reset

            notify_event(CI13XXX_CONTROLLER_RESET_EVENT)

         hw_device_state

            notify_event(CI13XXX_CONTROLLER_CONNECT_EVENT);

 

msm_udc_irq  kernel/drivers/usb/gadget/ci13xxx_msm.c ( request_irq(_udc_ctxt.irq, msm_udc_irq, IRQF_SHARED, pdev->name...)

  udc_irq

    isr_suspend_handler  ci13xxx_udc.c

       udc->driver->suspend(&udc->gadget);

       notify_event(CI13XXX_CONTROLLER_SUSPEND_EVENT);

       usb_phy_set_suspend(1)

       suspended=1

    isr_reset_handler   ci13xxx_udc.c

       notify_event(CI13XXX_CONTROLLER_RESUME_EVENT);

       usb_phy_set_suspend(0)

       udc->driver->resume(&udc->gadget);

       suspended=0

       usb_phy_set_power(udc->transceiver, 100);

           set_power()    otg.h

             msm_otg_set_power  msm_otg.c

               msm_otg_notify_charger(motg, mA);   msm_otg.c

                   msm_otg_notify_chg_type    msm_otg.c

                     power_supply_set_supply_type  power/power_supply_core.c

                       set_property(POWER_SUPPLY_PROP_TYPE)

                         otg_power_set_property_usb(POWER_SUPPLY_PROP_TYPE)

                   msm_otg_notify_power_supply      msm_otg.c

                      power_supply_set_online(psy, true))

                      power_supply_set_current_limit(100mA)

 

       _gadget_stop_activity   ci13xxx_udc.c

           android_disconnect

             composite_disconnect

             schedule_work()->android_work    "android_work: did not send uevent (0 0   (null))"

       hw_usb_reset();

    isr_resume_handler  

       notify_event(CI13XXX_CONTROLLER_RESUME_EVENT)

       usb_phy_set_suspend(0);

       udc->driver->resume(&udc->gadget);

    isr_tr_complete_handler  ci13xxx_udc.c

       driver->setup

         android_setup       android.c

            f->ctrlrequest(f, cdev, c);

              mtp_function_ctrlrequest()

            composite_setup(USB_REQ_GET_DESCRIPTOR USB_DT_DEVICE)   composite.c

            schedule_work() ->android_work  "android_work: sent uevent USB_STATE=CONNECTED"

     isr_reset_handler

        ...

          _gadget_stop_activity

             android_disconnect

               composite_disconnect

               schedule_work()->android_work "android_work: sent uevent USB_STATE=DISCONNECTED"

     isr_resume_handler

        ...

     isr_tr_complete_handler USB_REQ_SET_ADDRESS 1

     isr_tr_complete_handler driver->setup

        android_setup       android.c

          composite_setup(USB_REQ_GET_DESCRIPTOR USB_DT_DEVICE)   composite.c

          schedule_work()->android_work      "android_work: sent uevent USB_STATE=CONNECTED"

     isr_tr_complete_handler driver->setup

        android_setup

          composite_setup USB_REQ_GET_DESCRIPTOR USB_DT_CONFIG

     isr_tr_complete_handler driver->setup (USB_DT_STRING 3 times, vary)

        android_setup

          composite_setup USB_REQ_GET_DESCRIPTOR USB_DT_STRING

     isr_tr_complete_handler driver->setup           

        android_setup       android.c

          composite_setup(USB_REQ_GET_DESCRIPTOR USB_DT_DEVICE)   composite.c

     isr_tr_complete_handler driver->setup

        android_setup

          composite_setup USB_REQ_GET_DESCRIPTOR USB_DT_CONFIG

     isr_tr_complete_handler USB_REQ_SET_CONFIGURATION 1

     isr_tr_complete_handler driver->setup

        android_setup

          composite_setup USB_REQ_SET_CONFIGURATION   composite.c

             set_config

               set_alt

                 mtp_function_set_alt

                 adb_function_set_alt

               usb_gadget_vbus_draw() 

                  vbus_draw()

                    ci13xxx_vbus_draw  ci13xxx_udc.c

                        usb_phy_set_power

                          set_power

                            msm_otg_set_power

                               msm_otg_notify_charger(motg, mA);         "usb: Avail curr from USB = 500"

                                      msm_otg_notify_chg_type

                                            power_supply_set_supply_type

                                      msm_otg_notify_power_supply

     ...    

     isr_tr_complete_handler driver->setup

        android_setup

          composite_setup USB_REQ_GET_DESCRIPTOR USB_DT_STRING

          schedule_work()->android_work    "android_work: sent uevent USB_STATE=CONFIGURED"

 

 4.3 判读HS, FS
ci13xxx_udc.c
 udc_probe()
   udc->gadget.speed        = USB_SPEED_UNKNOWN;
   udc->gadget.max_speed    = USB_SPEED_HIGH;      //最大速度

isr_resume_handler()
  udc->gadget.speed = hw_port_is_high_speed() ? USB_SPEED_HIGH : USB_SPEED_FULL;
HS/FS是通过函数hw_port_is_high_speed()得到

 

4.4 判断Host/Device

msm_otg_start_peripheral(on=1)
这里通过判断是SDP_CHARGER,然后设成peripheral(device).

 

qpnp_chg_usb_usbin_valid_irq_handler: qpnp_chg_usb_usbin_valid_irq_handler usbin-valid triggered: 1 host_mode: 0
上面就可以打印出host_mode, 是通过下面函数得到
host_mode = qpnp_chg_is_otg_en_set(chip);

接OTG线后,可以通过如下中断处理函数看出
msm_pmic_id_irq(), 主要处理函数msm_pmic_id_status_w()

也可以通过函数得到msm_otg_read_pmic_id_state()

PMIC: ID clear     host
PMIC: ID set       device

 

5. 下面才是MTP的单独部分

5.1 start MTP server

android_work()发送uevent(USB_STATE=CONFIGURED)给上层

packages/providers/MediaProvider/src/com/android/providers/media/MtpReceiver.java

handleUsbState()

   intent = new Intent(context, MtpService.class);
   context.startService(intent);

 

packages/providers/MediaProvider/src/com/android/providers/media/MtpService.java

onStartCommand  
   manageServiceLocked()    MtpService.java  "starting MTP server in MTP mode"
      mServer = new MtpServer(mDatabase, mPtpMode);
          MtpServer(MtpDatabase database, boolean usePtp) frameworks/base/media/java/android/mtp/MtpServer.java
      mServer.start();  

 

frameworks/base/media/jni/android_mtp_MtpServer.cpp

native_setup   
   android_mtp_MtpServer_setup 
         int fd = open("/dev/mtp_usb", O_RDWR);
               mtp_open()    f_mtp.c
    MtpServer* server = new MtpServer(fd, getMtpDatabase(env, javaDatabase),..)

 

packages/providers/MediaProvider/src/com/android/providers/media/MtpService.java
mServer.start();    
   run()  frameworks/base/media/java/android/mtp/MtpServer.java
      native_run   frameworks/base/media/jni/android_mtp_MtpServer.cpp
      android_mtp_MtpServer_run
         server->run();
             MtpServer::run()   frameworks/av/media/mtp/MtpServer.cpp

 

5.2 主要处理函数

frameworks/av/media/mtp/MtpServer.cpp

MtpServer::run()   
infinite loop; read request; send data/response (write)
Read part
 mRequest.read(fd); 
    MtpRequestPacket::read()  (MtpRequestPacket    mRequest;)
      ::read(fd, mBuffer, mBufferSize); 
         mtp_read()    f_mtp.c
     
Write data/response
 mData.write
    MtpDataPacket::write(int fd)
       ::write(fd, mBuffer, mPacketSize);
          mtp_write()    f_mtp.c
 mResponse.write
    MtpResponsePacket::write
       ::write(fd, mBuffer, mPacketSize);
          mtp_write()    f_mtp.c
      

Event
MtpServer::doGetObject()
   ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
     mtp_ioctl() in f_mtp.c
MtpServer::doSendObject()
   ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
     mtp_ioctl() in f_mtp.c

MtpServer::sendObjectAdded/sendObjectRemoved/sendStoreAdded/sendStoreRemoved/sendDevicePropertyChanged
  MtpServer::sendEvent(MtpEventCode code, uint32_t param1)
    MtpEventPacket::write(int fd)
       ::ioctl(fd, MTP_SEND_EVENT, (unsigned long)&event);
          mtp_ioctl() in f_mtp.c
            mtp_send_event()

开始的几个操作

MTP_OPERATION_OPEN_SESSION (1002)             
MTP_OPERATION_GET_DEVICE_INFO (1001)          
MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED (9801)
MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED (9801)
MTP_OPERATION_GET_DEVICE_PROP_DESC (1014)     
MTP_OPERATION_GET_OBJECT_PROP_LIST (9805)

 

6. Debug方法

首先看看是哪部分出问题了

a. 检查main log, UsbDeviceManager, setEnabledFunctions()是否正常

b. 检查property,    persist.sys.usb.config,  sys.usb.config, sys.usb.state

c. 检查sysfs, functions and state, vid, pid

d. 检查kernel log, 能否看到sent uevent(USB_STATE=CONFIGURED), 同时看看main log, UsbDeviceManager

e. 检查main log/kernel, MTP server启动是否正常

f. 如果正常看看命令接收和response是否正常

针对不同出错地方,仔细看相应部分代码

 

调试工具

USB sniffer 方便看到所有数据,从协议上看看出错地方,尤其是可能是PC问题时

 

对比方法

换PC, 换手机的方法来对比测试

 

7. 正常log

mtp_bind_config                                                                     
msm_otg : USB exited from low power mode                                
msm_otg : chg_type = USB_SDP_CHARGER                                    
msm_hsusb msm_hsusb: vbus online                                                    
msm_hsusb msm_hsusb: CI13XXX_CONTROLLER_RESET_EVENT received                        
msm_hsusb msm_hsusb: CI13XXX_CONTROLLER_CONNECT_EVENT received                      
msm_hsusb msm_hsusb: reset                                                          
msm_otg : Avail curr from USB = 100                                     
android_work: android_work: did not send uevent (0 0   (null))                      
android_work: android_work: sent uevent USB_STATE=CONNECTED                         
msm_hsusb msm_hsusb: reset                                                          
android_work: android_work: sent uevent USB_STATE=DISCONNECTED                      
android_work: android_work: sent uevent USB_STATE=CONNECTED                         
msm_otg : Avail curr from USB = 500                                     
android_work: android_work: sent uevent USB_STATE=CONFIGURED    
MtpService: starting MTP server in MTP mode                   
mtp_open       

 

 

 

2019-05-09 18:46:36 zwlove5280 阅读数 737

有了上篇文章Andrid MTP之UsbService分析的基础,本片文章就来分析MTP,那么我们首先要搞清楚,如何切换MTP

MTP切换

通过上篇文章分析可知,当手机通过USB连接电脑的时候,会发送一个USBADB通知,而且会发送一个USB状态切换广播。

通知界面如下

在这里插入图片描述

当点击最后一个通知,就会出现如下的USB功能设置选项

在这里插入图片描述
当选择Transfer files选项的时候,就是USB开启了MTP模式。开启MTP的核心代码如下

mUsbManager = context.getSystemService(UsbManager.class);
mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MTP, true);

UsbManager的服务端实现为UsbService,而UsbServicesetCurrentFunction()方法在
Andrid MTP之UsbService分析中已经分析过,这里就简单的总结下UsbService切换MTP模式所做的工作:

  1. 设置sys.usb.config属性的值为mtp,adb,底层响应属性改变,切换到MTP功能。
  2. 上层mUEventObserver监听到USB状态改变,在手机通过USB连接到电脑的情况下,会生成两个通知(如上面的第一幅图所示),以及发送一个USB状态改变的广播。

那么今天我们分析的起点就是这个USB状态改变广播,发送广播的代码如下

        private void updateUsbStateBroadcastIfNeeded(boolean configChanged) {
            // send a sticky broadcast containing current USB state
            Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
                    | Intent.FLAG_RECEIVER_FOREGROUND);
            intent.putExtra(UsbManager.USB_CONNECTED, mConnected);
            intent.putExtra(UsbManager.USB_HOST_CONNECTED, mHostConnected);
            intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
            intent.putExtra(UsbManager.USB_DATA_UNLOCKED,
                    isUsbTransferAllowed() && mUsbDataUnlocked);
            intent.putExtra(UsbManager.USB_CONFIG_CHANGED, configChanged);

            if (mCurrentFunctions != null) {
                String[] functions = mCurrentFunctions.split(",");
                for (int i = 0; i < functions.length; i++) {
                    final String function = functions[i];
                    if (UsbManager.USB_FUNCTION_NONE.equals(function)) {
                        continue;
                    }
                    intent.putExtra(function, true);
                }
            }

            // send broadcast intent only if the USB state has changed
            // 如果usb状态没有改变并且配置也没有改变,就不发送广播
            if (!isUsbStateChanged(intent) && !configChanged) {
                if (DEBUG) {
                    Slog.d(TAG, "skip broadcasting " + intent + " extras: " + intent.getExtras());
                }
                return;
            }

            if (DEBUG) Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras());
            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
            mBroadcastedIntent = intent;
        }

那么,这个广播的接收者是谁呢?接收这个广播又干了啥呢?这些话题都是本文要讨论的,也就是MTP的实现。

MtpReceiver

MtpReceiver.java属于packages/providers/MediaProvider模块,MediaProvider这个模块还包括了音频扫描工作以及一个铃声的选择功能,当然最主要功能还是向外部提供手机存储中的数据,其中就包括向电脑端提供MTP数据。

public class MtpReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {// 处理开机广播
            // If we somehow fail to configure after boot, it becomes difficult to
            // recover usb state. Thus we always configure once on boot, but it
            // has no effect if Mtp is disabled or already configured.
            MtpServer.configure(false);
            final Intent usbState = context.registerReceiver(
                    null, new IntentFilter(UsbManager.ACTION_USB_STATE));
            if (usbState != null) {
                handleUsbState(context, usbState, true);
            }
        } else if (UsbManager.ACTION_USB_STATE.equals(action)) { // 处理USB状态改变的广播
            handleUsbState(context, intent, false);
        }
    }
}

MtpReceiver虽然处理了两种广播,但是殊途同归,最终都是调用同一个方法进行处理的。

那么接下来看下handleUsbState()方法如何处理USB状态改变的

    private void handleUsbState(Context context, Intent intent, boolean from_boot) {
        Bundle extras = intent.getExtras();
        boolean configured = extras.getBoolean(UsbManager.USB_CONFIGURED);
        boolean connected = extras.getBoolean(UsbManager.USB_CONNECTED);
        boolean mtpEnabled = extras.getBoolean(UsbManager.USB_FUNCTION_MTP);
        boolean ptpEnabled = extras.getBoolean(UsbManager.USB_FUNCTION_PTP);
        boolean unlocked = extras.getBoolean(UsbManager.USB_DATA_UNLOCKED);
        boolean configChanged = extras.getBoolean(UsbManager.USB_CONFIG_CHANGED);
		
		// 1.处理两种情况
		// 1.1 mtp/ptp模式 && 配置改变了
		// 1.2 mtp/ptp模式 && usb已连接 && usb没有完成配置工作
        if ((configChanged || (connected && !configured)) && (mtpEnabled || ptpEnabled)) {
            MtpServer.configure(ptpEnabled);
            // tell MediaProvider MTP is configured so it can bind to the service
            context.getContentResolver().insert(Uri.parse(
                    "content://media/none/mtp_connected"), null);
        } 
        // 2. mtp/ptp模式 && usb已经配置
		else if (configured && (mtpEnabled || ptpEnabled)) {
            if(from_boot && mServiceStarted)
                return; // fix the problem of repeated adding storage
            intent = new Intent(context, MtpService.class);
            intent.putExtra(UsbManager.USB_DATA_UNLOCKED, unlocked);
            if (ptpEnabled) {
                intent.putExtra(UsbManager.USB_FUNCTION_PTP, true);
            }
            if (DEBUG) { Log.d(TAG, "handleUsbState startService"); }
            context.startService(intent);
            mServiceStarted = true;
        } 
		// 3. usb断开链接 || 不是mtp/ptp模式
		else if (!connected || !(mtpEnabled || ptpEnabled)) {
            // Only unbind if disconnected or disabled.
            boolean status = context.stopService(new Intent(context, MtpService.class));
            if (DEBUG) { Log.d(TAG, "handleUsbState stopService status=" + status); }
            // tell MediaProvider MTP is disconnected so it can unbind from the service
            context.getContentResolver().delete(Uri.parse(
                    "content://media/none/mtp_connected"), null, null);
        }
    }

从上篇文章可知,USB连接时,状态是从DISCONNECTEDCONNECTED,再到CONFIGURED,因此这里处理的三种情况就非常好理解了。

handleUsbState的第步,对于设置MTP功能来说,这里是处理配置改变,或者已连接但是没有配置完成的情况

			// 1. 底层初始化,并进行USB配置工作
            MtpServer.configure(ptpEnabled);
            // 2. 通知MediaProvider,MTP已经配置完毕,所以可以绑定服务了
            // tell MediaProvider MTP is configured so it can bind to the service
            context.getContentResolver().insert(Uri.parse(
                    "content://media/none/mtp_connected"), null);

首先看下底层是如何进行配置的,调用MtpServer的静态方法configure()MtpServer.java是在frameworks/base/media/java/android/mtp/目录下,这是framework层专用于处理mtp操作的目录

public class MtpServer implements Runnable {
    static {
        System.loadLibrary("media_jni");
    }

    public static void configure(boolean usePtp) {
        native_configure(usePtp);
    }

    public static native final void native_configure(boolean usePtp);
}

MtpServer实现了Runnable接口,那么我们可以猜测,肯定会创建一个线程,那么用这个线程干嘛呢?后面会说道。

MtpServer类在加载的时候会加载一个名为media_jni库,Android 是基于Linux操作系统构建的,因此这个库的全名是libmedia_jni.so,这个库对应的路径是frameworks/base/media/jni目录,而本地方法native_configure()android_mtp_MtpServer.cpp文件中实现。

static void android_mtp_configure(JNIEnv *, jobject, jboolean usePtp) {
    MtpServer::configure(usePtp);
}

本地方法native_configure()在JNI层注册对应的函数为android_mtp_configure(),后面不再说明类似的对应情况,读者可自行查看源码查找。

原来是调用MtpServerconfigure()函数,而MtpServer.cpp是在frameworks/av/media/mtp目录下,对应的库是libmtp.so

IMtpHandle* MtpServer::sHandle = nullptr;

int MtpServer::configure(bool usePtp) {
    // 1. 初始化sHandle
    if (sHandle == nullptr) {
        bool ffs_ok = access(FFS_MTP_EP0, W_OK) == 0;
        sHandle = ffs_ok ? get_ffs_handle() : get_mtp_handle();
    }

    // 2. 调用sHandle的configure方法
    int ret = sHandle->configure(usePtp);
    if (ret) ALOGE("Failed to configure MTP driver!");

    // 3. 设置属性sys.usb.ffs.mtp.ready值为1
    android::base::SetProperty("sys.usb.ffs.mtp.ready", "1");

    return ret;
}

这里最重要的一步就是初始化sHandle变量,我的手机环境中调用的是get_mtp_handle()函数,这个函数的实现类为frameworks/av/media/mtp/MtpDevHandle.cpp

IMtpHandle *get_mtp_handle() {
    return new MtpDevHandle();
}

很简单,就是调用自己的构造方法

MtpDevHandle::MtpDevHandle()
    : mFd(-1) {};

更简单,就是给mFd变量赋值为-1

初始化sHandle变量后,接着会调用它的configure()函数来完成配置工作。

int MtpDevHandle::configure(bool) {
    // Nothing to do, driver can configure itself
    return 0;
}

根据注释可知,什么都不用做,驱动可以自己完成。

配置完成后,最后还把系统属性sys.usb.ffs.mtp.ready的值设置为1,这个属性在我的系统中好像并没有用,sHandler的初始化调用的不是get_ffs_handle()函数,我猜测这个属性是跟这个函数相关的吧。

至此,底层的配置已经完成,再回到handleUsbState的第步,接下来就是通知MediaProvider底层已经配置完毕,可以绑定服务了,绑定哪个服务呢?绑定服务干嘛呢?

public class MediaProvider extends ContentProvider {

    private static final int MTP_CONNECTED = 705;

    static {
        URI_MATCHER.addURI("media", "*/mtp_connected", MTP_CONNECTED);
    }

    public Uri insert(Uri uri, ContentValues initialValues) {
        int match = URI_MATCHER.match(uri);

        ArrayList<Long> notifyRowIds = new ArrayList<Long>();
        // 返回的newUri是null
        Uri newUri = insertInternal(uri, match, initialValues, notifyRowIds);
		
        // ...

        return newUri;
    }
    
    private Uri insertInternal(Uri uri, int match, ContentValues initialValues,
                               ArrayList<Long> notifyRowIds) {
        Uri newUri = null;

        switch (match) {
            // ...

            case MTP_CONNECTED:
                synchronized (mMtpServiceConnection) {
                    if (mMtpService == null) {
                        Context context = getContext();
                        // MTP is connected, so grab a connection to MtpService
                        context.bindService(new Intent(context, MtpService.class),
                                mMtpServiceConnection, Context.BIND_AUTO_CREATE);
                    }
                }

                // ...
                break;

            // ...
        }
        return newUri;
    }
}    

原来绑定的是MtpService,既然是绑定而不是启动,那么肯定是要与服务进行通信,看下mMtpServiceConnection的赋值操作

    private IMtpService mMtpService;

    private final ServiceConnection mMtpServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, android.os.IBinder service) {
            synchronized (this) {
                mMtpService = IMtpService.Stub.asInterface(service);
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName className) {
            synchronized (this) {
                mMtpService = null;
            }
        }
    };

原来是利用AIDL来实现通讯,IMtpService.java类就是使用AIDL语法自动生成的Java类,因此接口方法是定义在IMtpService.aidl中,可以看下这个文件的定义

package com.android.providers.media;

interface IMtpService
{
    void sendObjectAdded(int objectHandle);
    void sendObjectRemoved(int objectHandle);
}

从方法的命名可知,有两种类型的消息,一种是告诉MtpService数据库中增加了某些数据,一种是告诉MtpService数据库中删除了某些数据。例如,当手机中新增一个文件的时候,会使用sendObjectAdded()方法,而删除一个文件会使用另外一种方法。而MtpService在接收消息后,会向底层发送消息,底层会通知PC端。

现在看下handleUsbState的第步,对于MTP来说,就是处理USBCONFIGURED状态,处理逻辑如下

			// MtpService只允许启动一次
            if(from_boot && mServiceStarted)
                return; // fix the problem of repeated adding storage
            intent = new Intent(context, MtpService.class);
            intent.putExtra(UsbManager.USB_DATA_UNLOCKED, unlocked);
            if (ptpEnabled) {
                intent.putExtra(UsbManager.USB_FUNCTION_PTP, true);
            }
            if (DEBUG) { Log.d(TAG, "handleUsbState startService"); }
            context.startService(intent);
            mServiceStarted = true;

又是MtpService,只是这一次换了一个姿势,不是绑定,而是启动。注意参数unlocked的值,前面说过在选择MTP模式时候传入的值是true。这个值非常重要,如果为falsePC端是不会映射手机存储的数据的。

MtpService

终于,MtpService千呼万唤始出来,我们不分析MtpService的绑定过程,只要你懂AIDL就能明白,因此着重看下启动过程到底在干嘛

public class MtpService extends Service {

    @Override
    public void onCreate() {
        mStorageManager = this.getSystemService(StorageManager.class);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        UserHandle user = new UserHandle(ActivityManager.getCurrentUser());
        // 1. 保存所有已挂载的存储的信息
        synchronized (this) {
            // mVolueMap是String->StorageVolume的映射,String代表存储的路径,StorageVolume由StorageManager获取
            mVolumeMap = new HashMap<>();
            // mStroageMap是String->MtpStorage的映射,String代表存储路径,MtpStorage保存存储的信息
            mStorageMap = new HashMap<>();
            // 监听存储的挂载与卸载
            mStorageManager.registerListener(mStorageEventListener);
            mVolumes = StorageManager.getVolumeList(user.getIdentifier(), 0);
            for (StorageVolume volume : mVolumes) {
            	// 如果是已挂载状态
                if (Environment.MEDIA_MOUNTED.equals(volume.getState())) {
                    // 用mVolumeMap和mStorageMap保存信息
                    volumeMountedLocked(volume.getPath());
                } else {
                    Log.e(TAG, "StorageVolume not mounted " + volume.getPath());
                }
            }
        }

		// 2. 向底层进行映射
        synchronized (this) {
        	// mUnlocked值为true
            mUnlocked = intent.getBooleanExtra(UsbManager.USB_DATA_UNLOCKED, false);
            // 使用mUnlocked的值更新mMtpDisabled的值
            updateDisabledStateLocked();
            // mPtpMode在MTP模式下当然为false
            mPtpMode = (intent == null ? false
                    : intent.getBooleanExtra(UsbManager.USB_FUNCTION_PTP, false));
            String[] subdirs = null;
            if (mPtpMode) {
                // ...
            }
            // 获取主存储,这也就代表这MTP只映射主存储
            final StorageVolume primary = StorageManager.getPrimaryVolume(mVolumes);
            try {
                manageServiceLocked(primary, subdirs, user);
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, "Couldn't find the current user!: " + e.getMessage());
            }
        }

        return START_REDELIVER_INTENT;
    }

先看下onStartCommand()的第步,这一步是用几个变量保存所有已挂在的存储的信息,其中调用的是volumeMountedLocked()方法保存已挂载存储的信息

    private void volumeMountedLocked(String path) {
        for (int i = 0; i < mVolumes.length; i++) {
            StorageVolume volume = mVolumes[i];
            if (volume.getPath().equals(path)) {
            	// 用mVolumeMap保存已挂在的存储信息,是volume path到StorageVolume的映射
                mVolumeMap.put(path, volume);
                // 此时mMtpDisabled使用的是默认的初始值false
                if (!mMtpDisabled) {
                    // In PTP mode we support only primary storage
                    // 如果存储为主存储或者不是PTP模式时,保存存储的信息
                    // 注意,此时mPtpMode使用的也是默认的初始值false
                    if (volume.isPrimary() || !mPtpMode) {
                        addStorageLocked(volume);
                    }
                }
                break;
            }
        }
    }
    
    private void addStorageLocked(StorageVolume volume) {
    	// MtpStorage就是用来保存存储的各种信息,例如路径,剩余空间等等,之后会在JNI层映射MtpStroage保存的信息
        MtpStorage storage = new MtpStorage(volume, getApplicationContext());
        // 用mStorageMap保存storage path到MtpStorage的映射
        mStorageMap.put(storage.getPath(), storage);

		// ...
		
		// 此时sServerHolder还没有被赋值
        synchronized (MtpService.class) {
            if (sServerHolder != null) {
                sServerHolder.database.addStorage(storage);
                sServerHolder.server.addStorage(storage);
            }
        }
		
		// ...
    }

很简单,就是用几个变量保存了已挂载存储的信息。

接下来看下onStartCommand()的第

        synchronized (this) {
        	// mUnlocked值为true
            mUnlocked = intent.getBooleanExtra(UsbManager.USB_DATA_UNLOCKED, false);
            // 为变量mMtpDisabled 赋值为 !mUnlocked;
            updateDisabledStateLocked();
            // mPtpMode在MTP模式下当然为false
            mPtpMode = (intent == null ? false
                    : intent.getBooleanExtra(UsbManager.USB_FUNCTION_PTP, false));
            String[] subdirs = null;
            if (mPtpMode) {
                // ...
            }
            // 获取主存储,这也就代表着MTP只映射主存储
            final StorageVolume primary = StorageManager.getPrimaryVolume(mVolumes);
            try {
                manageServiceLocked(primary, subdirs, user);
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, "Couldn't find the current user!: " + e.getMessage());
            }
        }

几个参数就不多说了,直接看下manageServiceLocked()方法的实现,对于MTP模式来说,这个方法只针对主存储

    private void manageServiceLocked(StorageVolume primary, String[] subdirs, UserHandle user)
            throws PackageManager.NameNotFoundException {
        synchronized (this) {
        	// MtpServer只能被启动一次
            if (sServerHolder != null) {
                if (LOGD) {
                    Log.d(TAG, "Cannot launch second MTP server.");
                }
                // Previously executed MtpServer is still running. It will be terminated
                // because MTP device FD will become invalid soon. Also MtpService will get new
                // intent after that when UsbDeviceManager configures USB with new state.
                return;
            }

			// 1. 创建MTP在Java层和JNI层的的数据库操作代理类对象
            final MtpDatabase database = new MtpDatabase(this,
                    createPackageContextAsUser(this.getPackageName(), 0, user),
                    MediaProvider.EXTERNAL_VOLUME,
                    primary.getPath(), subdirs);
                
            // 2. 创建MtpServer,负责向底层发送信息
            String deviceSerialNumber = Build.SERIAL;
            if (Build.UNKNOWN.equals(deviceSerialNumber)) {
                deviceSerialNumber = "????????";
            }
            final MtpServer server =
                    new MtpServer(
                            database, // 保存了MtpDatabase对象,可以操作数据库
                            mPtpMode,
                            new OnServerTerminated(),
                            Build.MANUFACTURER, // MTP DeviceInfo: Manufacturer
                            Build.MODEL,        // MTP DeviceInfo: Model
                            "1.0",              // MTP DeviceInfo: Device Version
                            deviceSerialNumber,  // MTP DeviceInfo: Serial Number
                            );
			// MtpDatabase对象保存了MtpServer对象,可以通过MtpServer对象向底层发送消息                            
            database.setServer(server);
            
            // 3. 用sServerHolder同时保存server和database
            sServerHolder = new ServerHolder(server, database);

            // Need to run addStorageDevicesLocked after sServerHolder is set since it accesses
            // sServerHolder.
            // 4. 如果MTP开启,向底层映射StorageId
            if (!mMtpDisabled) {
                addStorageDevicesLocked();
            }
			// 5. 开启线程,循环读取请求并处理
            server.start();
        }
    }

首先看第步,创建MtpDatabase对象,MtpDatabaseJava层数据库操作的代理类,看下构造函数

public class MtpDatabase implements AutoCloseable {
	
	// 保存JNI层创建的MtpDatabase对象
    private long mNativeContext;

    private native final void native_setup();

	static {
        System.loadLibrary("media_jni");
    }
    
    public MtpDatabase(Context context, Context userContext, String volumeName, String storagePath,
            String[] subDirectories) {
        // 1. 创建底层的MyMtpDatabase对象,并用this.mNativeContext指向
        native_setup();

		// 2. 初始化操作
        mContext = context;
        mUserContext = userContext;
        mPackageName = context.getPackageName();
        // 用于操作MediaProvider的数据库封装类
        mMediaProvider = userContext.getContentResolver()
                .acquireContentProviderClient(MediaStore.AUTHORITY);
        // 保存存储的名字
        mVolumeName = volumeName;
        // 存储的路径
        mMediaStoragePath = storagePath;
        // 操作MTP对象的URI: content://media/{volumeName}/object
        mObjectsUri = Files.getMtpObjectsUri(volumeName);
        // 用于媒体文件扫描
        mMediaScanner = new MediaScanner(context, mVolumeName);

        mSubDirectories = subDirectories;
        if (subDirectories != null) {
			// ...
        }
		// 为了兼容版本,在高版本中这里没做任何事
        initDeviceProperties(context);
		
		// 系统默认没有这个属性这个属性
        mDeviceType = SystemProperties.getInt("sys.usb.mtp.device_type", 0);

        mCloseGuard.open("close");
    }
}    

MtpDatabase也是加载了libmedia_jni.so库,前面已经说过,这个库对应于frameworks/base/media/jni目录。Java层的MtpDatabase.java的本地方法对应的JNI层的实现类为android_mtp_MtpDatabase.cpp,本地方法native_setup()对应的实现如下

static jfieldID field_context;

static void
android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz)
{
	// 1. 创建MyMtpDatabase对象
    MyMtpDatabase* database = new MyMtpDatabase(env, thiz);
    // 2. 把MyMtpDatabase对象地址保存到Java层的MtpDatabase对象的mNativeContext变量中
    env->SetLongField(thiz, field_context, (jlong)database);
    // 如果有异常就打印log,然后清空异常
    checkAndClearExceptionFromCallback(env, __FUNCTION__);
}

这个函数首先创建一个MyMtpDatabase对象,然后保存在Java层的MtpDatabase对象的变量中,那么这个field_context指的是哪个变量呢?原来,在JNI层的库加载的时候,会调用JNI_OnLoad()函数,这个函数在frameworks/base/media/jni/android_media_MediaPlayer.cpp文件中定义,而在JNI_OnLoad()函数中,会调用MtpDatabase.cppregister_android_mtp_MtpDatabase()函数

int register_android_mtp_MtpDatabase(JNIEnv *env)
{
    jclass clazz;

    clazz = env->FindClass("android/mtp/MtpDatabase");
    if (clazz == NULL) {
        ALOGE("Can't find android/mtp/MtpDatabase");
        return -1;
    }
    
	// ...
	
	field_context = env->GetFieldID(clazz, "mNativeContext", "J");
    if (field_context == NULL) {
        ALOGE("Can't find MtpDatabase.mNativeContext");
        return -1;
    }
    
    // ...
}

现在我们应该就明白了,field_context其实代表的就是Java层的MtpDatabase类的mNativeContext变量。

读者一定要懂得JNI的基本知识,本文并不会对JNI的基本知识做过多解释。

再来看下android_mtp_MtpDatabase_setup()函数的第步,构造MyMtpDatabase对象,注意,构造函数传入了Java层的MtpDatabase对象

MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client)
    :   mDatabase(env->NewGlobalRef(client)),
        mIntBuffer(NULL),
        mLongBuffer(NULL),
        mStringBuffer(NULL)
{
    // create buffers for out arguments
    // we don't need to be thread-safe so this is OK
    jintArray intArray = env->NewIntArray(3);
    if (!intArray) {
        return; // Already threw.
    }
    mIntBuffer = (jintArray)env->NewGlobalRef(intArray);
    jlongArray longArray = env->NewLongArray(2);
    if (!longArray) {
        return; // Already threw.
    }
    mLongBuffer = (jlongArray)env->NewGlobalRef(longArray);
    // Needs to be long enough to hold a file path for getObjectFilePath()
    jcharArray charArray = env->NewCharArray(PATH_MAX + 1);
    if (!charArray) {
        return; // Already threw.
    }
    mStringBuffer = (jcharArray)env->NewGlobalRef(charArray);
}

这里初始化了四个全局变量,注意,其中mDatabase指向Java层MtpDatabase对象。

那么,我们回顾下MtpDatabase.javaJNI层的初始化到底做了啥,首先创建了JNI层的MyMtpDatabase对象,在这个对象中用mDatabase保存了JavaMtpDatabase对象,而之后,Java层的MtpDatabase对象的变量mNativeContext保存了JNI层的MyMtpDatabase对象的地址,这就相当于相互保存了。那么,为何要这样设计呢?当我们把Java层的MtpDatabase对象传递到JNI层,就可以通过这个对象获取到JNI层的MyMtpDatabase对象,这样就可以操作JNI层的数据,操作完成后,由于MyMtpDatabase对象保存了Java层的MtpDatabase对象,就可以回调上层的方法。源码中就是这个套路,我们可以学以致用。

再回到manageServiceLocked的第步,创建MtpServer对象,看下它的构造函数

    public MtpServer(
            MtpDatabase database,
            boolean usePtp,
            Runnable onTerminate,
            String deviceInfoManufacturer,
            String deviceInfoModel,
            String deviceInfoDeviceVersion,
            String deviceInfoSerialNumber) {
        mDatabase = Preconditions.checkNotNull(database);
        mOnTerminate = Preconditions.checkNotNull(onTerminate);
        // 创建底层的MtpServer对象,并用this.mNativeContext指向该对象
        native_setup(
                database,
                usePtp,
                deviceInfoManufacturer,
                deviceInfoModel,
                deviceInfoDeviceVersion,
                deviceInfoSerialNumber);
        database.setServer(this);
    }

构造方法中最重要的一步就是调用本地方法进行初始化,实现方法在android_mtp_MtpServer.cpp

static void
android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jboolean usePtp,
        jstring deviceInfoManufacturer,
        jstring deviceInfoModel,
        jstring deviceInfoDeviceVersion,
        jstring deviceInfoSerialNumber)
{
	// 1. 把jstring转换为本地字符串
    const char *deviceInfoManufacturerStr = env->GetStringUTFChars(deviceInfoManufacturer, NULL);
    const char *deviceInfoModelStr = env->GetStringUTFChars(deviceInfoModel, NULL);
    const char *deviceInfoDeviceVersionStr = env->GetStringUTFChars(deviceInfoDeviceVersion, NULL);
    const char *deviceInfoSerialNumberStr = env->GetStringUTFChars(deviceInfoSerialNumber, NULL);

	// 2. 创建MtpServer对象
    MtpServer* server = new MtpServer(getMtpDatabase(env, javaDatabase),
            usePtp, AID_MEDIA_RW, 0664, 0775,
            MtpString((deviceInfoManufacturerStr != NULL) ? deviceInfoManufacturerStr : ""),
            MtpString((deviceInfoModelStr != NULL) ? deviceInfoModelStr : ""),
            MtpString((deviceInfoDeviceVersionStr != NULL) ? deviceInfoDeviceVersionStr : ""),
            MtpString((deviceInfoSerialNumberStr != NULL) ? deviceInfoSerialNumberStr : ""));
            
	// 3. 释放本地字符串
    if (deviceInfoManufacturerStr != NULL) {
        env->ReleaseStringUTFChars(deviceInfoManufacturer, deviceInfoManufacturerStr);
    }
    if (deviceInfoModelStr != NULL) {
        env->ReleaseStringUTFChars(deviceInfoModel, deviceInfoModelStr);
    }
    if (deviceInfoDeviceVersionStr != NULL) {
        env->ReleaseStringUTFChars(deviceInfoDeviceVersion, deviceInfoDeviceVersionStr);
    }
    if (deviceInfoSerialNumberStr != NULL) {
        env->ReleaseStringUTFChars(deviceInfoSerialNumber, deviceInfoSerialNumberStr);
    }
	// 4. 保存MtpServer对象的地址到Java层MtpServer对象的mNativeContext中
    env->SetLongField(thiz, field_MtpServer_nativeContext, (jlong)server);
}

第一步,是把Java层的字符串转化为native字符串,第三步是释放这里本地字符串资源,这都是JNI的基本操作,不多说。

第二步是创建底层的MtpServer对象,然后第四步是用Java层的MtpServer对象的mNativeContext保存底层的MtpServer对象。

那么,重点关注第二步,看它如何创建底层MtpServer对象

    MtpServer* server = new MtpServer(getMtpDatabase(env, javaDatabase),
            usePtp, AID_MEDIA_RW, 0664, 0775,
            MtpString((deviceInfoManufacturerStr != NULL) ? deviceInfoManufacturerStr : ""),
            MtpString((deviceInfoModelStr != NULL) ? deviceInfoModelStr : ""),
            MtpString((deviceInfoDeviceVersionStr != NULL) ? deviceInfoDeviceVersionStr : ""),
            MtpString((deviceInfoSerialNumberStr != NULL) ? deviceInfoSerialNumberStr : ""));

第一个参数是由getMtpDatase()获取的,看下函数声明

// in android_mtp_MtpDatabase.cpp
extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);

从注释中我们就可以看出,这个实现在android_mtp_MtpDatabase.cpp

MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
    return (MtpDatabase *)env->GetLongField(database, field_context);
}

database指的就是Java层的MtpDatabase对象,这里不就是获取JNI层的MyMtpDatabase对象吗?

现在看下MtpServer的构造函数

MtpServer::MtpServer(MtpDatabase* database, bool ptp,
                    int fileGroup, int filePerm, int directoryPerm,
                    const MtpString& deviceInfoManufacturer,
                    const MtpString& deviceInfoModel,
                    const MtpString& deviceInfoDeviceVersion,
                    const MtpString& deviceInfoSerialNumber)
    :   mDatabase(database),
        mPtp(ptp),
        mFileGroup(fileGroup),
        mFilePermission(filePerm),
        mDirectoryPermission(directoryPerm),
        mDeviceInfoManufacturer(deviceInfoManufacturer),
        mDeviceInfoModel(deviceInfoModel),
        mDeviceInfoDeviceVersion(deviceInfoDeviceVersion),
        mDeviceInfoSerialNumber(deviceInfoSerialNumber),
        mSessionID(0),
        mSessionOpen(false),
        mSendObjectHandle(kInvalidObjectHandle),
        mSendObjectFormat(0),
        mSendObjectFileSize(0),
        mSendObjectModifiedTime(0)
{
}

很简单的初始化,这里比较重要的一步就是在底层的MtpServer对象中用mDatabase指向JNI层的MyMtpDatabase对象,透过这个对象,可以操作上层的MtpDatabse对象。

再回到manageServiceLocked()函数的第步,代码如下

			sServerHolder = new ServerHolder(server, database);
			
            // Need to run addStorageDevicesLocked after sServerHolder is set since it accesses
            // sServerHolder.
            if (!mMtpDisabled) {
            	// 向底层映射存储的信息
                addStorageDevicesLocked();
            }

从注释中可以看出,只有sServerHolder被赋值,才需要运行addStorageDevicesLocked()方法,这个方法是向底层映射存储的信息

    private void addStorageDevicesLocked() {
        if (mPtpMode) {
			// ...
        } else {
            for (StorageVolume volume : mVolumeMap.values()) {
                addStorageLocked(volume);
            }
        }
    }

前面已经说过,mVolumeMap保存的是已挂载的存储,通过遍历它来调用addStorageLocked()方法

    private void addStorageLocked(StorageVolume volume) {
        MtpStorage storage = new MtpStorage(volume, getApplicationContext());
        mStorageMap.put(storage.getPath(), storage);

        if (storage.getStorageId() == StorageVolume.STORAGE_ID_INVALID) {
            Log.w(TAG, "Ignoring volume with invalid MTP storage ID: " + storage);
            return;
        } else {
            Log.d(TAG, "Adding MTP storage 0x" + Integer.toHexString(storage.getStorageId())
                    + " at " + storage.getPath());
        }
		
        synchronized (MtpService.class) {
            if (sServerHolder != null) {
            	// MtpDatabase对象的mStorageMap变量保存MtpStroage对象
                sServerHolder.database.addStorage(storage);
                // MtpServer对象通过本地方法向底层映射MtpStroage对象信息
                sServerHolder.server.addStorage(storage);
            }
        }
		
		// ...
    }

这里主要看下sServerHolder保存的MtpServer对象,是如何向底层进行映射操作的

    public void addStorage(MtpStorage storage) {
        native_add_storage(storage);
    }

JNI层的实现如下

static void
android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage)
{
    Mutex::Autolock autoLock(sMutex);
	// 1. 获取Java层的MtpServer对象保存的JNI层对象
    MtpServer* server = getMtpServer(env, thiz);
    if (server) {
    	// 获取Java层的MtpStorage对象的各种变量的值
    	// int mStorageId;
        jint storageID = env->GetIntField(jstorage, field_MtpStorage_storageId);
        // String mPath;
        jstring path = (jstring)env->GetObjectField(jstorage, field_MtpStorage_path);
        // String mDescription;
        jstring description = (jstring)env->GetObjectField(jstorage, field_MtpStorage_description);
        // long mReserveSpace;
        jlong reserveSpace = env->GetLongField(jstorage, field_MtpStorage_reserveSpace);
        // boolean mRemovable;
        jboolean removable = env->GetBooleanField(jstorage, field_MtpStorage_removable);
        // long mMaxFileSize;
        jlong maxFileSize = env->GetLongField(jstorage, field_MtpStorage_maxFileSize);

		// 把path转换为本地字符串
        const char *pathStr = env->GetStringUTFChars(path, NULL);
        if (pathStr != NULL) {
        	// 把description转换为本地字符串
            const char *descriptionStr = env->GetStringUTFChars(description, NULL);
            if (descriptionStr != NULL) {
            	// 2. 利用从Java层MtpStorage对象获取到的所有信息,创建底层的MtpStorage对象
                MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr,
                        reserveSpace, removable, maxFileSize);
                // 3. 底层的MtpServer对象保存这个刚创建的MtpStorage对象
                server->addStorage(storage);
                env->ReleaseStringUTFChars(path, pathStr);
                env->ReleaseStringUTFChars(description, descriptionStr);
            } else {
                env->ReleaseStringUTFChars(path, pathStr);
            }
        }
    } else {
        ALOGE("server is null in add_storage");
    }
}

从实现中可以看出,首先是获取了JNI层的MtpServer对象,然后获取Java层的MtpStorage对象的各种信息,并利用这些信息创建了JNI层的MtpStorage对象,最后把创建的MtpStorage对象保存到MtpServer对象中。前两步比较简单,只看下最后一步是如何保存的

MtpStorageList      mStorages;

void MtpServer::addStorage(MtpStorage* storage) {
    Mutex::Autolock autoLock(mMutex);
    // 1. 保存
    mStorages.push(storage);
    // 2. 发送Event
    sendStoreAdded(storage->getStorageID());
}

void MtpServer::sendStoreAdded(MtpStorageID id) {
    ALOGV("sendStoreAdded %08X\n", id);
    sendEvent(MTP_EVENT_STORE_ADDED, id);
}

void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
    ALOGD("sendEvent %d\n", mSessionOpen);
    if (mSessionOpen) {
        mEvent.setEventCode(code);
        mEvent.setTransactionID(mRequest.getTransactionID());
        mEvent.setParameter(1, param1);
        if (mEvent.write(sHandle))
            ALOGE("Mtp send event failed: %s", strerror(errno));
    }
}

底层的MtpServer对象用mStorages保存了存储信息,然后利用MTP协议,向电脑端发送了一个MTP Event事件,用于告知电脑端可以映射存储了,电脑端会发送各种命令用于获取存储中的信息,从而在电脑端建立一个文件系统的映射。

现在看下manageServiceLocked()的第步,代码如下

server.start();

看下MtpServerstart()方法

    public void start() {
        Thread thread = new Thread(this, "MtpServer");
        thread.start();
    }

启动一个线程,看下这个线程做了什么

    @Override
    public void run() {
    	// 循环读取MTP指令,执行相应的动作
        native_run();
        // 异常处理工作
        native_cleanup();
        mDatabase.close();
        mOnTerminate.run();
    }

native_run()是在一个新的线程中开启一个无限循环处理PC发过来的请求,而后面的几步就是处理异常情况,我们这里主要集中分析处理请求的过程,看下native_fun()方法的JNI实现

static void
android_mtp_MtpServer_run(JNIEnv *env, jobject thiz)
{
    MtpServer* server = getMtpServer(env, thiz);
    if (server)
    	// 开启无限循环,从节点读取命令,然后执行命令
        server->run();
    else
        ALOGE("server is null in run");
}

调用了底层的MtpServerrun()方法,注意,这个方法是在一个Java创建的线程中执行的,主要就是不断读取MTP节点,解析命令,然后执行相应动作

void MtpServer::run() {
    if (!sHandle) {
        ALOGE("MtpServer was never configured!");
        return;
    }
	// 1. 打开/dev/mtp_usb节点
    if (sHandle->start()) {
        ALOGE("Failed to start usb driver!");
        sHandle->close();
        return;
    }

	// 2. 循环处理事务
    while (1) {
    	// 2.1 用sHandle读取请求
        int ret = mRequest.read(sHandle);
        if (ret < 0) {
            ALOGE("request read returned %d, errno: %d", ret, errno);
            if (errno == ECANCELED) {
                // return to top of loop and wait for next command
                continue;
            }
            break;
        }
        // 解析操作码
        MtpOperationCode operation = mRequest.getOperationCode();
        // 解析事务ID
        MtpTransactionID transaction = mRequest.getTransactionID();

        ALOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
        // FIXME need to generalize this
        // 下面操作码表示是PC端发数据过来
        bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
                    || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
                    || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
                    || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
        if (dataIn) { // 如果是PC端发数据过来,就解析数据
            int ret = mData.read(sHandle);
            if (ret < 0) {
                ALOGE("data read returned %d, errno: %d", ret, errno);
                if (errno == ECANCELED) {
                    // return to top of loop and wait for next command
                    continue;
                }
                break;
            }
            ALOGV("received data:");
        } else {
            mData.reset();
        }

		// 2.2 处理请求
        if (handleRequest()) {
        	// 2.3 成功处理后进行响应
            if (!dataIn && mData.hasData()) { // 代表手机端在发数据
                mData.setOperationCode(operation);
                mData.setTransactionID(transaction);
                ALOGV("sending data:");
                ret = mData.write(sHandle);
                if (ret < 0) {
                    ALOGE("request write returned %d, errno: %d", ret, errno);
                    if (errno == ECANCELED) {
                        // return to top of loop and wait for next command
                        continue;
                    }
                    break;
                }
            }
			// 设置相应的事务ID
            mResponse.setTransactionID(transaction);
            ALOGV("sending response %04X", mResponse.getResponseCode());
            // 写一些数据,并用sHandle发送
            ret = mResponse.write(sHandle);
            const int savedErrno = errno;
            if (ret < 0) {
                ALOGE("request write returned %d, errno: %d", ret, errno);
                if (savedErrno == ECANCELED) {
                    // return to top of loop and wait for next command
                    continue;
                }
                break;
            }
        } else {
            ALOGV("skipping response\n");
        }
    }

	// 处理异常情况,例如断开连接
    // commit any open edits
    int count = mObjectEditList.size();
    for (int i = 0; i < count; i++) {
        ObjectEdit* edit = mObjectEditList[i];
        commitEdit(edit);
        delete edit;
    }
    mObjectEditList.clear();

    if (mSessionOpen)
        mDatabase->sessionEnded();

    sHandle->close();
}

处理过程大致分为两步,首先是打开节点,然后循环读取节点并处理。打开节点这个过程不分析,比较简单,着重看下处理过程。

在分析处理过程之前,这里有几个MTP的概念,首先是transcation,也就是事务的意思,transcation包括三个阶段

  1. 操作请求阶段: 也就是PC端向手机端发送请求
  2. 数据传输阶段: 这个阶段是可选的,也就是请求之后可能PC可能需要传输数据
  3. 相应阶段: 手机处理了PC的请求后,会进行响应。

另外一个概念就是session,也就是会话的意思,一次session代表一次连接环境,有的操作必须要在这个环境中进行,例如只有在MTP已连接的情况下才能向PC发送事件,但是有些操作并不依赖于session,例如PC端要获取设备信息。

读取请求

那么,先来看下transcation的请求阶段,手机端首先调用mRequest.read(sHandle)读取请求

int MtpRequestPacket::read(IMtpHandle *h) {
	// 1. 读取节点的数据到mBuffer中
    int ret = h->read(mBuffer, mBufferSize);
    if (ret < 0) {
        // file read error
        return ret;
    }

    // request packet should have 12 byte header followed by 0 to 5 32-bit arguments
    // 2. 解析数据的大小和参数的个数
    const size_t read_size = static_cast<size_t>(ret);
    if (read_size >= MTP_CONTAINER_HEADER_SIZE
            && read_size <= MTP_CONTAINER_HEADER_SIZE + 5 * sizeof(uint32_t)
            && ((read_size - MTP_CONTAINER_HEADER_SIZE) & 3) == 0) {
        mPacketSize = read_size;
        mParameterCount = (read_size - MTP_CONTAINER_HEADER_SIZE) / sizeof(uint32_t);
    } else {
        ALOGE("Malformed MTP request packet");
        ret = -1;
    }
    return ret;
}

读取的详细操作就不分析了,这里需要关心下读取的数据格式。请求的数据包包括12字节的头部,然后跟随0个或者5个参数,每个参数是4个字节。头文件中定义如下

// Container Offsets
#define MTP_CONTAINER_LENGTH_OFFSET             0	//4个字节
#define MTP_CONTAINER_TYPE_OFFSET               4	//2个字节
#define MTP_CONTAINER_CODE_OFFSET               6	//2个字节
#define MTP_CONTAINER_TRANSACTION_ID_OFFSET     8	//4个字节
#define MTP_CONTAINER_PARAMETER_OFFSET          12	//参数起始位置
#define MTP_CONTAINER_HEADER_SIZE               12	//头部总共12字节

处理请求

读取之后就需要处理请求

bool MtpServer::handleRequest() {
    Mutex::Autolock autoLock(mMutex);
	
	// 获取操作码
    MtpOperationCode operation = mRequest.getOperationCode();
    MtpResponseCode response;

    mResponse.reset();

    if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
        // FIXME - need to delete mSendObjectHandle from the database
        ALOGE("expected SendObject after SendObjectInfo");
        mSendObjectHandle = kInvalidObjectHandle;
    }

	// 获取操作类型
    int containertype = mRequest.getContainerType();
    if (containertype != MTP_CONTAINER_TYPE_COMMAND) {
        ALOGE("wrong container type %d", containertype);
        return false;
    }

    ALOGV("got command %s (%x)", MtpDebug::getOperationCodeName(operation), operation);

    switch (operation) {
        case MTP_OPERATION_GET_DEVICE_INFO:
            response = doGetDeviceInfo();
            break;
        case MTP_OPERATION_OPEN_SESSION:
            response = doOpenSession();
            break;
        case MTP_OPERATION_RESET_DEVICE:
        case MTP_OPERATION_CLOSE_SESSION:
            response = doCloseSession();
            break;
        case MTP_OPERATION_GET_STORAGE_IDS:
            response = doGetStorageIDs();
            break;
         case MTP_OPERATION_GET_STORAGE_INFO:
            response = doGetStorageInfo();
            break;
        case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
            response = doGetObjectPropsSupported();
            break;
        case MTP_OPERATION_GET_OBJECT_HANDLES:
            response = doGetObjectHandles();
            break;
        case MTP_OPERATION_GET_NUM_OBJECTS:
            response = doGetNumObjects();
            break;
        case MTP_OPERATION_GET_OBJECT_REFERENCES:
            response = doGetObjectReferences();
            break;
        case MTP_OPERATION_SET_OBJECT_REFERENCES:
            response = doSetObjectReferences();
            break;
        case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
            response = doGetObjectPropValue();
            break;
        case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
            response = doSetObjectPropValue();
            break;
        case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
            response = doGetDevicePropValue();
            break;
        case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
            response = doSetDevicePropValue();
            break;
        case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
            response = doResetDevicePropValue();
            break;
        case MTP_OPERATION_GET_OBJECT_PROP_LIST:
            response = doGetObjectPropList();
            break;
        case MTP_OPERATION_GET_OBJECT_INFO:
            response = doGetObjectInfo();
            break;
        case MTP_OPERATION_GET_OBJECT:
            response = doGetObject();
            break;
        case MTP_OPERATION_GET_THUMB:
            response = doGetThumb();
            break;
        case MTP_OPERATION_GET_PARTIAL_OBJECT:
        case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
            response = doGetPartialObject(operation);
            break;
        case MTP_OPERATION_SEND_OBJECT_INFO:
            response = doSendObjectInfo();
            break;
        case MTP_OPERATION_SEND_OBJECT:
            response = doSendObject();
            break;
        case MTP_OPERATION_DELETE_OBJECT:
            response = doDeleteObject();
            break;
        case MTP_OPERATION_GET_OBJECT_PROP_DESC:
            response = doGetObjectPropDesc();
            break;
        case MTP_OPERATION_GET_DEVICE_PROP_DESC:
            response = doGetDevicePropDesc();
            break;
        case MTP_OPERATION_SEND_PARTIAL_OBJECT:
            response = doSendPartialObject();
            break;
        case MTP_OPERATION_TRUNCATE_OBJECT:
            response = doTruncateObject();
            break;
        case MTP_OPERATION_BEGIN_EDIT_OBJECT:
            response = doBeginEditObject();
            break;
        case MTP_OPERATION_END_EDIT_OBJECT:
            response = doEndEditObject();
            break;
        default:
            ALOGE("got unsupported command %s (%x)",
                    MtpDebug::getOperationCodeName(operation), operation);
            response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
            break;
    }

    if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
        return false;
    // 设置响应码
    mResponse.setResponseCode(response);
    return true;
}

这里处理了各种操作码对应的请求,包括PC端执行的初始化动作,如果需要了解每一步做了什么,就需要了解MTP协议的内容。

响应请求

最后就是响应阶段。

			// 在handleRequest()中设置的
			mResponse.setResponseCode(response);
			
			// 设置事务ID,代表完成了一次事务
            mResponse.setTransactionID(transaction);
            // 发送
            ret = mResponse.write(sHandle);

相应数据和请求数据的格式是一样的,这里这是了响应码和事务ID,当然还可以附带参数,还有类型,对于响应应该就是MTP_CONTAINER_TYPE_RESPONSE,最后就是长度,这个可以根据是否有参数进行计算出来的。

总结

由于受专业知识限制,本文无法更深入的分析底层操作,在工作中也确实遇到了与底层实现有关的问题,最后还是请教了专业的人士给出了解决方案。

MTP协议内容很多,本文只是抛砖引玉,工作中你可能只会遇到协议中的某一部分内容,因此如果搞清楚了流程,我想那一部分的分析就比较简单了。

另外,跟着这本文进行更深入的分析,我们就会发现精彩的JNI层到上层的回调,这不失为一个好的学习机会。

感想

在写本文的时候,其进行了很多构思,总想这把所有事情解释明白(除了驱动和内核操作),但是这需要巨大的篇幅,我想读者很可能也没那么多耐心读完。那么我只能把源码的"架构图"尽量完整的描述,以求抛砖引玉。

另外我并没有用一个UML图来描绘整体的"架构图",因为我们实际中用到的只是某一部分,而且我也并没有打算分析所有的流程,因此就省略了。

参考

https://en.wikipedia.org/wiki/Media_Transfer_Protocol#Comparison_with_USB_Mass_Storage
https://www.cnblogs.com/skywang12345/p/3474206.html
MTP文档
https://www.usb.org/documents

2016-10-26 17:04:50 q1183345443 阅读数 979

相关代码

  1. packages/providers/MediaProvider/src/com/android/providers/media/MtpReceiver.java    
  2. packages/providers/MediaProvider/src/com/android/providers/media/MtpService.java    
  3. packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java    
  4. frameworks/base/media/java/android/mtp/MtpServer.java    
  5. frameworks/base/media/java/android/mtp/MtpDatabase.java    
  6. frameworks/base/media/java/android/mtp/MtpStorage.java    
  7. frameworks/base/media/jni/android_mtp_MtpServer.cpp    
  8. frameworks/base/media/jni/android_mtp_MtpDatabase.cpp    
  9. frameworks/av/media/mtp/MtpServer.h    
  10. frameworks/av/media/mtp/MtpServer.cpp
  11. frameworks/av/media/mtp/MtpDatabase.h

MTP主要处理逻辑


一、UsbDevicemanager发送usb状态广播

UsbDevicemanager发送ACTION_USB_STATE的广播,来通知usb状态的变化

private void updateUsbStateBroadcast() {  
    // send a sticky broadcast containing current USB state  
    boolean UsbDataUnlocked = isUsbTransferAllowed() && mUsbDataUnlocked ;  
    Intent intent = new Intent(UsbManager.ACTION_USB_STATE);  
    intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING  
            | Intent.FLAG_RECEIVER_FOREGROUND);  
    intent.putExtra(UsbManager.USB_CONNECTED, mConnected);  
    intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);  
    intent.putExtra(UsbManager.USB_DATA_UNLOCKED, UsbDataUnlocked);  
  
    if (mCurrentFunctions != null) {  
        String[] functions = mCurrentFunctions.split(",");  
        for (int i = 0; i < functions.length; i++) {  
            intent.putExtra(functions[i], true);//这里是各个functions的状态  
        }  
    }  
  
    if (DEBUG) Slog.d(TAG, "broadcasting " + intent + " , connected: " + mConnected  
                            + " , configured: " + mConfigured + " , UsbDataUnlocked: " + UsbDataUnlocked  
                            + " , CurrentFunctions: " + mCurrentFunctions);  
    mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);  


MtpReceiver.java

这个属于应用的代码,先监听开机广播,然后注册ACTION_USB_STATE广播,最后在handleUsbState的时候会启动MtpService

   private void handleUsbState(Context context, Intent intent) {
        Bundle extras = intent.getExtras();
        boolean connected = extras.getBoolean(UsbManager.USB_CONFIGURED);
        boolean mtpEnabled = extras.getBoolean(UsbManager.USB_FUNCTION_MTP);
        boolean ptpEnabled = extras.getBoolean(UsbManager.USB_FUNCTION_PTP);
        boolean unlocked = extras.getBoolean(UsbManager.USB_DATA_UNLOCKED);
        // Start MTP service if USB is connected and either the MTP or PTP function is enabled
        if (connected && (mtpEnabled || ptpEnabled)) {
            intent = new Intent(context, MtpService.class);
            intent.putExtra(UsbManager.USB_DATA_UNLOCKED, unlocked);
            if (ptpEnabled) {
                intent.putExtra(UsbManager.USB_FUNCTION_PTP, true);
            }
            if (DEBUG) { Log.d(TAG, "handleUsbState startService"); }
            context.startService(intent);
            // tell MediaProvider MTP is connected so it can bind to the service
            context.getContentResolver().insert(Uri.parse(
                    "content://media/none/mtp_connected"), null);
        } else {
            boolean status = context.stopService(new Intent(context, MtpService.class));
            if (DEBUG) { Log.d(TAG, "handleUsbState stopService status=" + status); }
            // tell MediaProvider MTP is disconnected so it can unbind from the service
            context.getContentResolver().delete(Uri.parse(
                    "content://media/none/mtp_connected"), null, null);
        }
    }


MtpService.java

public int onStartCommand(Intent intent, int flags, int startId) {
        mUnlocked = intent.getBooleanExtra(UsbManager.USB_DATA_UNLOCKED, false);//该值为true默认MTP显示文件,否则就算默认MTP也是不显示磁盘内容的
        if (LOGD) { Log.d(TAG, "onStartCommand intent=" + intent + " mUnlocked=" + mUnlocked); }
        synchronized (mBinder) {
            updateDisabledStateLocked();
            mPtpMode = (intent == null ? false
                    : intent.getBooleanExtra(UsbManager.USB_FUNCTION_PTP, false));
            String[] subdirs = null;
            if (mPtpMode) {
                int count = PTP_DIRECTORIES.length;
                subdirs = new String[count];
                for (int i = 0; i < count; i++) {
                    File file =
                            Environment.getExternalStoragePublicDirectory(PTP_DIRECTORIES[i]);
                    // make sure this directory exists
                    file.mkdirs();
                    subdirs[i] = file.getPath();
                }
            }
            final StorageVolume primary = StorageManager.getPrimaryVolume(mVolumes);
            if (mDatabase != null) {
                mDatabase.setServer(null);
            }
            mDatabase = new MtpDatabase(this, MediaProvider.EXTERNAL_VOLUME,
                    primary.getPath(), subdirs);
            manageServiceLocked();
        }

        return START_REDELIVER_INTENT;
    }


private void manageServiceLocked() {
        final boolean isCurrentUser = UserHandle.myUserId() == ActivityManager.getCurrentUser();
        if (mServer == null && isCurrentUser) {
            Log.d(TAG, "starting MTP server in " + (mPtpMode ? "PTP mode" : "MTP mode"));
            Log.d(TAG,"mMtpDisabled " + mMtpDisabled);
            mServer = new MtpServer(mDatabase, mPtpMode);
            mDatabase.setServer(mServer);
            if (!mMtpDisabled) {
               addStorageDevicesLocked();
            }
            mServer.start();
        } else if (mServer != null && !isCurrentUser) {
            Log.d(TAG, "no longer current user; shutting down MTP server");
            // Internally, kernel will close our FD, and server thread will
            // handle cleanup.
            mServer = null;
            mDatabase.setServer(null);
        }
    }
    private void addStorageDevicesLocked() {
        if (mPtpMode) {
            // In PTP mode we support only primary storage
            final StorageVolume primary = StorageManager.getPrimaryVolume(mVolumes);
            final String path = primary.getPath();
            if (path != null) {
                String state = mStorageManager.getVolumeState(path);
                if (Environment.MEDIA_MOUNTED.equals(state)) {
                    addStorageLocked(mVolumeMap.get(path));
                }
            }
        } else {
            for (StorageVolume volume : mVolumeMap.values()) {
                addStorageLocked(volume);
            }
        }
    }



MTP简介
2018-05-25 13:52:40 ossoom 阅读数 650

1.
<1>MTP(Media Transfer Protocol),即媒体传输协议,由微软(Microsoft)开发。Linux有支持MTP的软件包。
根据MTP协议,MTP的使用者包括两个部分,分别是Initiator和Responder

Initiator:主要是指USB Host,例如PC机,笔记本等。协议规定所有MTP操作只能由Initator发起。
Responder:一般是诸如数码相机、智能手机等存储媒体文件的设备。Responder在MTP中的作用就是处理Initator发起的请求。同时,它还会根据自身状态的变化发送Event以通知Initiator。

<2>与很多协议一样,MTP也有自己的协议栈

由图可知,MTP协议栈由下到上分别是:
物理层:物理层在MTP协议中用来传输数据。目前有三种物理层可供MTP使用。它们分别是USB:其主要特点是传输文件,同步媒体文件时速度快,而且可以边工作边充电,这是目前用的最多的一种方式;IP:基于IP的MTP(简称MTP/IP)将通过UPnP来匹配和发现设备。它是家庭网络中是最理想的传输方式;Bluetooth:MTP/BT是最省电,同时也是速度最慢的一种传输方式,用处较少。
传输层:MTP中,数据传输格式遵循PTP协议(PTP协议是MTP协议前身)
命令层:实现了MTP协议中的各种命令。

<3>如上文所述,MTP采用命令-应答方式来工作(Initator发送命令给Responder处理,Responser反馈处理结果),这种方式的主要特点有:
所有MTP命令均以Package(数据包)的方式在设备两端进行传递。
Initiator必须接收到前一条消息的处理结果(不论是成功还是超时)后,才能发送下一条消息。
下面我们将以PC通过MTP打开一个文件为例,按顺序介绍其中涉及到几个主要MTP命令:
当设备第一次连接上PC后,Initiator(即PC)首先会发送一个名为GetDeviceInfo的请求以获取设备的信息,这些信息包括设备所支持PTP版本的程度,以百分号表示(默认是100)、所支持的MTP命令(Operation Supported)、所支持的Event类型等。
接着PC端会发送OpenSession命令以创建一个会话,该会话一直保持到设备从PC上断开为止。此后所有命令(除GetDeviceInfo命令外)必须在此会话存活期间才能发送。会话在MTP协议中由SessionID来标识,它是一个32位的无符号整型,由PC选择并传给手机。
PC端如果要进行文件操作的话,必须从根目录开始定位目标文件。由于Windows的特殊性,手机内部存储卡在windows系统中显示为盘符。注意,如果手机内部有两块存储卡的话(如内部存储卡和外部sd卡),Windows中会显示为两个盘符。PC端需要通过GetStorageIDs命令返回某个盘符对应的StorageID。在MTP中,StorageID是一个32位无符号整型,每一个StorageID代表了一个逻辑盘符。
PC端可以根据上一步的StorageID号,利用GetStorageInfo操作去获取存储设备的信息,例如剩余存储空间、文件系统类型、访问权限等。
接着,PC就会通过GetObjectHandles命令来获取此盘符下的文件和子目录的Object Handles(一个Object Handle代表一个文件或目录。该值由Responder生成并保证唯一性)。有了Object Handle,PC就可以操作这些文件或目录了,例如继续通过GetObjectHandles获取某个目录中子文件和子目录的信息。
假设现在需拷贝一个文件到手机上,那么PC会通过SendObjectInfo命令将文件信息(如文件名、文件大小)等传递给手机。而手机需要检查目标目录是否有足够的空间和对应权限。
如果一切正常,PC将通过SendObject把数据传递给手机。真正写文件到设备存储空间的则是手机中的Responder。Android实现的MTP还会在媒体文件传输完毕后,将信息更新到媒体数据库中。
除此之外,PC还可利用SetObjectPropValue 命令来设置文件的各种属性值,如Audio BitRate(比特率),Sample Rate(采样率),Number Of Channels(声道)等。
MTP协议,各种MTP命令,可参考文献《MTP Specification v1.0.pdf》。协议对各种命令都有非常精确的描述。
http://files.cnblogs.com/files/skywang12345/mtp_specification_v1.0.pdf

2.
下面我们来看MTP在Android平台中的实现。

Android中的MTP
Android从3.0开始集成MTP功能,主要原因有:
手机要支持UMS(usb mass storage)的话,必须有一个sd卡,因为sd卡往往采用Windows支持的分区格式。如果想把内部存储空间通过UMS挂载到Windows上,则内部存储空间需采用特定的分区格式。这对某些手机而言根本不可行。因为内部存储空间本身可能是一个设备,它们采用统一的分区格式。不能因为需要使用UMS,而再增加一块特定分区格式的存储设备。
UMS挂载到PC后,PC操作系统拥有绝对控制权。此时,Android系统将无法操作这些设备。这对越来越高级的Android版本而言是不可接受的。

Android中的MTP和已有的MediaProvider模块结合紧密,以更好体现“Media Transfer”的特性。

Android MTP架构图

总结:在”PC和Android设备”连接后,MtpReceiver会监听”USB连接/断开广播”。到收到广播时,会根据”USB的连接状态,MTP/PTP的Enable状态”决定对MTP的处理。如果是连上状态,而且MTP服务是Enable的,则MtpReceiver会启动MtpService服务;并且通知MediaProvider。MtpService会启动MtpServer(Java层)。MtpServer(Java)层会调用底层的JNI函数。在JNI中,会打开MTP文件节点”/dev/mtp_usb”,然后MtpServer(JNI层)会不断的从中读取消息并进行处理。

2015-03-10 16:10:35 Fybon 阅读数 948


mtp性能分类:

file transfer

emumeration

delete objects 

mtp性能影响因素:

cpu    cpu io 带宽

emmc : MLC / TLC

usb :  usb 2.0 /  usb 3.0

file-system  : ext4 / f2fs / fuse / wrapfs ..........    

测试PC性能:CPU /  机械硬盘、 固态硬盘 ?


mtp 性能优化方向:

参考论文:  http://www.sersc.org/journals/IJCA/vol4_no3/6.pdf



Over !


MTP协议开发入门

阅读数 11057

Windows XP安装MTP驱动

阅读数 45015

USB MTP和数据库

阅读数 266

USB MTP和数据库

博文 来自: zoosenpin
没有更多推荐了,返回首页