from : http://blog.youkuaiyun.com/column/details/android-usb.html
Framework层收到消息后,又向vold发送了volume mount的消息,所以vold层又继续着处理这个消息,先看下大概处理流程:
同FrameWork层阻塞在等待vold的消息一样,vold层也在等待着收到 FrameWork层的消息,不过是调用select函数百阻塞,因为这个还有内核可能会有其它的连接请求的到来等,所以不能阻塞。
我们看下代码:
- void SocketListener::runListener() {
- .
- .
- .
- if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
- SLOGE("select failed (%s)", strerror(errno));
- sleep(1);
- continue;
- }
- .
- .
- .
- if (FD_ISSET(fd, &read_fds)) {
- pthread_mutex_unlock(&mClientsLock);
- if (!onDataAvailable(*it)) {
- close(fd);
- pthread_mutex_lock(&mClientsLock);
- delete *it;
- it = mClients->erase(it);
- pthread_mutex_unlock(&mClientsLock);
- }
- FD_CLR(fd, &read_fds);
- pthread_mutex_lock(&mClientsLock);
- continue;
- }
- }
收到消息后,调用onDataAvailable,这里这个函数的实现是在FrameworkListener类中,在onDataAvailable中接收数据,并调用dispatchCommand对分发命令:
- void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
- .
- .
- .
- for (i = mCommands->begin(); i != mCommands->end(); ++i) {
- FrameworkCommand *c = *i;
- if (!strcmp(argv[0], c->getCommand())) {
- if (c->runCommand(cli, argc, argv)) {
- SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
- }
- goto out;
- }
- }
- }
mCommands中的命令是在什么时候加进去的?回顾下CommandListener的初始化,我们注册了很多的命令,对的,就是在注册这些命令的时候加进去的,这里传下来的命令是volume mount ,所以调用 VolumeCmd::runCommand
- int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
- int argc, char **argv) {
- .
- .
- .
- else if (!strcmp(argv[1], "mount")) {
- if (argc != 3) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>", false);
- return 0;
- }
- rc = vm->mountVolume(argv[2]);
- .
- .
- .
- }
针对mount命令,调用mountVolume,mountVolume中继续调用mountVol:
- int Volume::mountVol() {
- dev_t deviceNodes[4];
- int n, i, rc = 0;
- char errmsg[255];
- if (getState() == Volume::State_NoMedia) {
- snprintf(errmsg, sizeof(errmsg),
- "Volume %s %s mount failed - no media",
- getLabel(), getMountpoint());
- mVm->getBroadcaster()->sendBroadcast(
- ResponseCode::VolumeMountFailedNoMedia,
- errmsg, false);
- errno = ENODEV;
- return -1;
- } else if (getState() != Volume::State_Idle) {
- errno = EBUSY;
- return -1;
- }
- if (isMountpointMounted(getMountpoint())) {
- SLOGW("Volume is idle but appears to be mounted - fixing");
- setState(Volume::State_Mounted);
- // mCurrentlyMountedKdev = XXX
- return 0;
- }
- n = getDeviceNodes((dev_t *) &deviceNodes, 4);
- if (!n) {
- SLOGE("Failed to get device nodes (%s)\n", strerror(errno));
- return -1;
- }
- for (i = 0; i < n; i++) {
- char devicePath[255];
- int result = 0;
- const char *disktype = "fat";
- sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]),
- MINOR(deviceNodes[i]));
- SLOGI("%s being considered for volume %s\n", devicePath, getLabel());
- errno = 0;
- setState(Volume::State_Checking);
- result = Fat::check(devicePath);
- if(result)
- {
- result = Ntfs::check(devicePath);
- if(!result)
- {
- disktype = "ntfs";
- }
- }
- if (result) {
- if (errno == ENODATA) {
- SLOGW("%s does not contain a FAT(Ntfs) filesystem\n", devicePath);
- continue;
- }
- errno = EIO;
- /* Badness - abort the mount */
- SLOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
- setState(Volume::State_Idle);
- return -1;
- }
- /*
- * Mount the device on our internal staging mountpoint so we can
- * muck with it before exposing it to non priviledged users.
- */
- errno = 0;
- if(0 == strcmp(disktype, "fat"))
- {
- if (Fat::doMount(devicePath, "/mnt/secure/staging", false, false,false, 1000, 1015, 0702, true)) {
- SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
- continue;
- }
- }
- else if(0 == strcmp(disktype, "ntfs"))
- {
- if (Ntfs::doMount(devicePath, "/mnt/secure/staging", false, false,false, 1000, 1015, 0702, true)) {
- SLOGE("%s failed to mount via NTFS (%s)\n", devicePath, strerror(errno));
- continue;
- }
- }
- SLOGI("Device %s, target %s mounted @ /mnt/secure/staging", devicePath, getMountpoint());
- protectFromAutorunStupidity();
- if (createBindMounts()) {
- SLOGE("Failed to create bindmounts (%s)", strerror(errno));
- umount("/mnt/secure/staging");
- setState(Volume::State_Idle);
- return -1;
- }
- /*
- * Now that the bindmount trickery is done, atomically move the
- * whole subtree to expose it to non priviledged users.
- */
- if (doMoveMount("/mnt/secure/staging", getMountpoint(), false)) {
- SLOGE("Failed to move mount (%s)", strerror(errno));
- umount("/mnt/secure/staging");
- setState(Volume::State_Idle);
- return -1;
- }
- setState(Volume::State_Mounted);
- mCurrentlyMountedKdev = deviceNodes[i];
- return 0;
- }
- SLOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
- setState(Volume::State_Idle);
- return -1;
- }
mountVol中首先检票Volume的状态,这里面必须为State_Idle状态才会进行后面的操作,这里有一点需要注意下,我们知道,在DirectVolume::handleDiskAdded的时候 向FrameWork层发送VolumeDiskInserted消息,这个时候 FrameWork层才下发volume mount消息,但是这个时候Voleme的State为State_Pending,要等到内核将这块设备的所有分区的add消息发出并调用完handlePartitionAdded才将Volume的状态设为State_Idle,这里会不会发生这种情况:FrameWork消息已经发下来了要进行mount了,但add分区的消息还没处理完,这个时候Volume的状态仍为State_Pending,所以在这里mountVol检查状态的时候不正确,直接返回失败,因为在我们的项目中发现有的时候存储设备会挂载不上,所以这里加了一个延时处理,状态不对时,睡眠一会再处理。状态检查之后调用getDeviceNodes获取有多少分区,然后对所有分区一一进行挂载,注意挂载的时候是先挂载到/mnt/secure/staging,然后现调用doMoveMount移动到挂载点。