android usb挂载分析---vold处理FrameWork层发出的消息

来源:互联网 发布:阿里云校园扶持计划 编辑:程序博客网 时间:2024/05/17 21:54

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移动到挂载点。