前言
这两天正好在研究一个通过MTP模式拷贝文件到手机速度慢的问题,顺便把整个MTP架构学了一遍,所以写一篇文章记录并分享一下。
1.MTP传输原理
主要分为三部分
1.手机端的MediaProvider进程
2.USB线
3.PC端的MTP客户端
简单的描述就是:
手机端的MediaProvider进程不断的监听USB端口根据MTP协议读写数据
PC端的MTP客户端也是不断的监听USB端口根据MTP协议读写数据
PC端的MTP客户端代码是微软写的,我们只需要研究手机端MediaProvider进程中MTP相关的代码即可。
2.MediaProvider中MTP功能整体架构图
整体架构比较清晰,如果你看过Binder驱动,你会发现结构非常类似,Java->JNI->Native->设备节点->驱动程序。
2.1 Java层
/frameworks/base/media/java/android/mtp/MtpServer.java
public class MtpServer implements Runnable {
public void start() {
Thread thread = new Thread(this, "MtpServer");
thread.start();
}
@Override
public void run() {
native_run();
native_cleanup();
mDatabase.close();
mOnTerminate.run();
}
}
MtpServer的代码很简单,本身就是实现了Runnable接口,然后新启一个“MtpServer”的线程运行MtpServer,调用native_run。
2.2 JNI层
/frameworks/base/media/jni/android_mtp_MtpServer.cpp
android_mtp_MtpServer_run(JNIEnv *env, jobject thiz)
{
MtpServer* server = getMtpServer(env, thiz);
if (server)
server->run();
else
ALOGE("server is null in run");
}
JNI没有太多的逻辑,调用MtpServer->run(),更多是实现在Native层的MtpServer.cpp
2.3 Native层
/frameworks/av/media/mtp/MtpServer.cpp
MtpServer::MtpServer(IMtpDatabase* database, int controlFd, bool ptp,
const char *deviceInfoManufacturer,
const char *deviceInfoModel,
const char *deviceInfoDeviceVersion,
const char *deviceInfoSerialNumber)
{
//我们的设备不支持FFS_MTP_EP0
bool ffs_ok = access(FFS_MTP_EP0, W_OK) == 0;
if (ffs_ok) {
bool aio_compat = android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false);
mHandle = aio_compat ? new MtpFfsCompatHandle(controlFd) : new MtpFfsHandle(controlFd);
} else {
mHandle = new MtpDevHandle();//默认都是走这里
}
}
void MtpServer::run() {
//启动MtpDevHandle
if (mHandle->start(mPtp)) {
ALOGE("Failed to start usb driver!");
mHandle->close();
return;
}
...
//请注意这里是死循环
while (1) {
//读取收到的MTP协议
int ret = mRequest.read(mHandle);
MtpOperationCode operation = mRequest.getOperationCode();
MtpTransactionID transaction = mRequest.getTransactionID();
...
//根据上面获得的MTP协议进行处理
if (handleRequest()) {
if (!dataIn && mData.hasData()) {
mData.setOperationCode(operation);
mData.setTransactionID(transaction);
ALOGV("sending data:");
ret = mData.write(mHandle);
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;
}
}
mResponse.setTransactionID(transaction);
ALOGV("sending response %04X", mResponse.getResponseCode());
ret = mResponse.write(mHandle);
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");
}
}
...
}
MtpServer.cpp的逻辑就相对复杂,主要是通过MtpDevHandle去和MTP驱动沟通,下面就是MtpDevHandle.cpp
/frameworks/av/media/mtp/MtpDevHandle.cpp
#include "MtpDevHandle.h"
namespace android {
constexpr char mtp_dev_path[] = "/dev/mtp_usb";
MtpDevHandle::MtpDevHandle()
: mFd(-1) {};
MtpDevHandle::~MtpDevHandle() {}
int MtpDevHandle::read(void *data, size_t len) {
return ::read(mFd, data, len);
}
int MtpDevHandle::write(const void *data, size_t len) {
return ::write(mFd, data, len);
}
int MtpDevHandle::receiveFile(mtp_file_range mfr, bool) {
return ioctl(mFd, MTP_RECEIVE_FILE, reinterpret_cast<unsigned long>(&mfr));
}
int MtpDevHandle::sendFile(mtp_file_range mfr) {
return ioctl(mFd, MTP_SEND_FILE_WITH_HEADER, reinterpret_cast<unsigned long>(&mfr));
}
int MtpDevHandle::sendEvent(mtp_event me) {
return ioctl(mFd, MTP_SEND_EVENT, reinterpret_cast<unsigned long>(&me));
}
int MtpDevHandle::start(bool /* ptp */) {
mFd.reset(TEMP_FAILURE_RETRY(open(mtp_dev_path, O_RDWR)));
if (mFd == -1) return -1;
return 0;
}
void MtpDevHandle::close() {
mFd.reset();
}
} // namespace android
如果看过Binder机制的人,看这个代码是不是非常熟悉,open,ioctl,close,因为mtp驱动也是一个字符型的驱动。
2.4 驱动层
驱动层的代码我就补贴了,我担心很多人看不懂,但是为了方便理解驱动层的做的事,我会用文字描述一下从PC端通过MTP拷贝文件到手机中过程。
3 从PC端通过MTP拷贝1.txt文件到手机中的流程
1.MtpServer通过MtpDevHandle从MTP驱动中获得了1.txt接受的信息,包含了1.txt文件的信息以及存储的路径信息。
2.MtpServer根据第1步中的信息创建了一个1.txt在手机中。
3.MtpServer通过MtpDevHandle将1.txt文件路径传给了MTP驱动
4.MTP驱动不断读取USB驱动中的数据并写入到1.txt文件中
MTP驱动层伪代码
read_data = null;
write_data = null;
while(usb有数据 || write_data != null){
read_data = read(usb);//从usb驱动中读数据
if(write_data != null) {
write("1.txt", write_data);
}
if(read_data != null) {
write_data = read_data;
}
}
总结
以上就是我对MTP的架构的总结,细心的读者会说android系统层很多功能的套路都差不多啊,是的,其实你只要掌握了基础Kernel驱动知识,Native开发,JNI开发,Java开发,Android Framework,Binder机制(Binder HIDL VNDBinder),会发现看很多模块自己会学的越来越快。以下就会我画的一个简单ION架构图,从看代码到画图,也就十几分钟,后续我会详细讲一下ION架构,敬请期待。