Android MTP存储映射分析

Android MTP之服务端UsbService启动分析了mtp服务端的启动,Android MTP模式切换分析分析了mtp的切换,并且在成功切换后,会发送一个usb状态改变的广播,系统接收这个广播来实现手机存储到pc端的映射.简单来说,通过mtp,pc端能看到手机存储的内容.

接收usb状态改变广播的是MtpReceiver,它属于packages/providers/MediaProvider模块,它会调用handleUsbState()方法处理usb状态改变

// packages/providers/MediaProvider/src/com/android/providers/media/MtpReceiver.java
    private void handleUsbState(Context context, Intent intent) {
        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 isCurrentUser = UserHandle.myUserId() == ActivityManager.getCurrentUser();

        if (configured && (mtpEnabled || ptpEnabled)) {
            if (!isCurrentUser)
                return;
            intent = new Intent(context, MtpService.class);
            intent.putExtra(UsbManager.USB_DATA_UNLOCKED, unlocked);
            if (ptpEnabled) {
                intent.putExtra(UsbManager.USB_FUNCTION_PTP, true);
            }
            context.startService(intent);
        } else if (!connected || !(mtpEnabled || ptpEnabled)) {
            boolean status = context.stopService(new Intent(context, MtpService.class));
        }
    }

很简单,usb连接上就启动MtpService,断开连接就停止它.而启动MtpService就是为了把存储映射到PC端

// packages/providers/MediaProvider/src/com/android/providers/media/MtpService.java
    public synchronized int onStartCommand(Intent intent, int flags, int startId) {
		// ...

        // 获取主存储
        final StorageVolume primary = StorageManager.getPrimaryVolume(mVolumes);
        // 映射主存储
        // 注意: 第二个参数subdirs是针对ptp模式,而对于mtp模式,值为null
        startServer(primary, subdirs);
        return START_REDELIVER_INTENT;
    }

    private synchronized void startServer(StorageVolume primary, String[] subdirs) {
    	// ...
        synchronized (MtpService.class) {
			// ...
            
            // 1. 建立上层与JNI层的关系.
            // MtpDatabase是MTP操作的上层接口
            final MtpDatabase database = new MtpDatabase(this, subdirs);
            // MtpServer是JNI和上层交互的接口
            final MtpServer server =
                    new MtpServer(database, fd, mPtpMode,
                            new OnServerTerminated(), Build.MANUFACTURER,
                            Build.MODEL, "1.0");
            database.setServer(server);
            sServerHolder = new ServerHolder(server, database);

            // Add currently mounted and enabled storages to the server
            // 从这里可以看出,只有数据解锁了,才会映射存储到PC
            if (mUnlocked) {
                if (mPtpMode) {
                    
                } else {
                    // 2. 通知PC端开始映射存储
                    // MtpStorage->MtpServer->底层MtpServer->发送Event给pc
                    for (StorageVolume v : mVolumeMap.values()) {
                        addStorage(v);
                    }
                }
            }
            // 3.开启线程,处理PC端请求
            server.start();
        }
    }

映射主存储到pc端,分为三步

  1. 建立上层与JNI层的关系.这是在创建MtpDatabase, MtpServer对象的时候完成的.
  2. 通过addStorage()通知pc端来添加存储.这是通过向pc端发送mtp event来完成的.
  3. 开启线程,处理pc端请求.例如pc端要获取存储中有哪些文件,文件的大小,等等.

MtpDatabase类是上层mtp操作的接口,它使用MtpStorageManager来完成文件系统的操作,使用MediaProvider来获取音频数据,并且文件操作会同步到MediaProvider
MtpServer类是上层与JNI层交互的接口,MtpDatabase正是通过MtpServer实现对底层的操作.

建立上层与JNI层的映射关系,是在MtpDatabase, MtpServer的构造函数中完成,本文略过,如果你有jni基础,这段代码非常简单.

首先看下如何通知pc端添加存储,这是通过addStorage()实现的

// packages/providers/MediaProvider/src/com/android/providers/media/MtpService.java
    private void addStorage(StorageVolume volume) {
        synchronized (MtpService.class) {
            if (sServerHolder != null) {
            	// 使用MtpDatabase这个上层的MTP操作接口
                sServerHolder.database.addStorage(volume);
            }
        }
    }

正如前面所说,MtpDatabase是上层mtp操作接口,通知pc的任务,肯定必须先经过它

// frameworks/base/media/java/android/mtp/MtpDatabase.java
    public void addStorage(StorageVolume storage) {
        // MtpStorageManager保存这个存储
        MtpStorage mtpStorage = mManager.addMtpStorage(storage);
        mStorageMap.put(storage.getPath(), mtpStorage);
        // 通过MtpServer,通知JNI层
        if (mServer != null) {
            mServer.addStorage(mtpStorage);
        }
    }

正如前面所说,MtpServer是上层与jni交互的接口,MtpDatabase内部通过它来打通jni层,通知pc

// frameworks/base/media/java/android/mtp/MtpServer.java
    public void addStorage(MtpStorage storage) {
        native_add_storage(storage);
    }

native_add_storage()是在JNI层实现的,实现类为android_mtp_MtpServer.cpp

mtp的framework层是在frameworks/base/media/java
mtp的jni层是在frameworks/base/media/jni
支持mtp的jni层的实现在frameworks/av/media/mtp

// frameworks/base/media/jni/android_mtp_MtpServer.cpp
static void
android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage)
{
    Mutex::Autolock autoLock(sMutex);

    MtpServer* server = getMtpServer(env, thiz);
    if (server) {
        // 获取应用层MtpStorage对象的各种属性
        jint storageID = env->GetIntField(jstorage, field_MtpStorage_storageId);
        jstring path = (jstring)env->GetObjectField(jstorage, field_MtpStorage_path);
        jstring description = (jstring)env->GetObjectField(jstorage, field_MtpStorage_description);
        jboolean removable = env->GetBooleanField(jstorage, field_MtpStorage_removable);
        jlong maxFileSize = env->GetLongField(jstorage, field_MtpStorage_maxFileSize);

        const char *pathStr = env->GetStringUTFChars(path, NULL);
        if (pathStr != NULL) {
            const char *descriptionStr = env->GetStringUTFChars(description, NULL);
            if (descriptionStr != NULL) {
                // 创建底层的MtpStorage对象
                MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr,
                        removable, maxFileSize);
                // 通过底层的MtpServer添加这个MtpStorage对象,它会向pc端发送一个Event,通pc端,存储已经添加
                server->addStorage(storage);
                env->ReleaseStringUTFChars(path, pathStr);
                env->ReleaseStringUTFChars(description, descriptionStr);
            } else {
                env->ReleaseStringUTFChars(path, pathStr);
            }
        }
    } else {
        ALOGE("server is null in add_storage");
    }
}

这里转到了底层MtpServeraddStorage()

// frameworks/av/media/mtp/MtpServer.cpp

void MtpServer::addStorage(MtpStorage* storage) {
    std::lock_guard lg(mMutex);
    // 保存
    mStorages.push_back(storage);
    // 向PC发送存储添加事件
    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) {
    if (mSessionOpen) {
        mEvent.setEventCode(code);
        mEvent.setTransactionID(mRequest.getTransactionID());
        mEvent.setParameter(1, param1);
        if (mEvent.write(mHandle))
            ALOGE("Mtp send event failed: %s", strerror(errno));
    }
}

到此,mtp的frameworks层的c++代码的工作就已经完成,剩下就是驱动层的任务了.本人的水平只能分析到jni层,到这里就不再继续深究了.

假设现在pc端已经收到这个添加存储的event,那么pc就会再次发送一系列的请求,这些请求就是用来获取存储的信息,包括存储里文件的信息.这样一来,pc端就能显示手机端存储的信息了,这就是存储的映射.

那么手机端是如何处理这些请求的呢?在分析MtpService#startServer()时,还有最后一步没有分析,通过MtpServer#start()方法启动一个线程来处理pc请求

// frameworks/base/media/java/android/mtp/MtpServer.java
    public void start() {
        Thread thread = new Thread(this, "MtpServer");
        thread.start();
    }
    
    @Override
    public void run() {
        // 底层MtpServer执行无线循环,读取pc请求,并处理
        native_run();
        // 如果处理请求发生错误,那么就会执行下面的三个清理动作
        native_cleanup();
        mDatabase.close();
        mOnTerminate.run();
    }

可以看到,这里是启动了一个线程,来执行native_run()

// frameworks/base/media/jni/android_mtp_MtpServer.cpp
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");
}

它把这个处理pc请求的任务交给了MtpServer.cpprun()方法

void MtpServer::run() {
    // 启动usb driver
    if (mHandle->start(mPtp)) {
        ALOGE("Failed to start usb driver!");
        mHandle->close();
        return;
    }
	// 无线循环处理pc请求,除非发生错误
    while (1) {
    	// 1. 读取请求
        int ret = mRequest.read(mHandle);
		
		// 2. 解析请求
        MtpOperationCode operation = mRequest.getOperationCode();
        MtpTransactionID transaction = mRequest.getTransactionID();
        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) {
            int ret = mData.read(mHandle);
            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();
        }
		
		// 3. 处理请求
        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");
        }
    }

    // 发生错误,就保存修改
    int count = mObjectEditList.size();
    for (int i = 0; i < count; i++) {
        ObjectEdit* edit = mObjectEditList[i];
        commitEdit(edit);
        delete edit;
    }
    mObjectEditList.clear();
	
	// 发生错误,退出
    mHandle->close();
}

这里的代码是在Java层开启的线程中执行的,而且这里使用了一个无限循环来处理pc的请求,具体的代码我就不在深究了.

你可能感兴趣的:(Android,MTP)