Android—— 4.2 Vold挂载管理_DirectVolume/Volume (五)

在前文Android—— 4.2 Vold挂载管理_VolumeManager (三) 中解析了VolumeManager是怎么样抽取Volume实例以及DirectVolume与Volume之间的关系,在上篇Android——
4.2 Vold挂载管理_NetlinkManager (四)
中从kernel开始调用到handleBlockEvent,这里解析一下Vold挂载的真正操作,也就是Volume的操作!

撰写不易,转载请注明出处:http://blog.csdn.net/jscese/article/details/38736481

一.DirectVolume抽象

源码位置:/system/vold/DirectVolume.cpp ,这个类是作为真正的Volume的一个抽象操作类,先看构造:

DirectVolume::DirectVolume(VolumeManager *vm, const char *label,
                           const char *mount_point, int partIdx) :
              Volume(vm, label, mount_point) {//同时构造父类实例
    mPartIdx = partIdx;//记录分区索引

    mPaths = new PathCollection();//路径容器
    for (int i = 0; i < MAX_PARTITIONS; i++) //google默认最多分区数为4
        mPartMinors[i] = -1;
    mPendingPartMap = 0;
    mDiskMajor = -1;
    mDiskMinor = -1;
    mDiskNumParts = 0;

    setState(Volume::State_NoMedia);//初始设置状态
}

上一篇有解析到DirectVolume的handleBlockEvent:

int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
    const char *dp = evt->findParam("DEVPATH"); //获取事件的设备路径

    PathCollection::iterator  it; //这个DirectVolume的Path容器,容器在构建时初始化,在VolumeManager中根据vold.fstab添加path
    for (it = mPaths->begin(); it != mPaths->end(); ++it) {
        if (!strncmp(dp, *it, strlen(*it))) { //筛选符合此次事件的Path,如果没有匹配的就退出这个DirectVolume实例的处理,在VolumeManager中交由下一个DirectVolume来处理,依次类推
            /* We can handle this disk */
            int action = evt->getAction();
            const char *devtype = evt->findParam("DEVTYPE");

            if (action == NetlinkEvent::NlActionAdd) { //事件类型有好几种,这里单以Add 添加来分析
                int major = atoi(evt->findParam("MAJOR"));
                int minor = atoi(evt->findParam("MINOR"));
                char nodepath[255];

                snprintf(nodepath,
                         sizeof(nodepath), "/dev/block/vold/%d:%d",
                         major, minor);
                if (createDeviceNode(nodepath, major, minor)) { //创建节点
                    SLOGE("Error making device node '%s' (%s)", nodepath,
                                                               strerror(errno));
                }
                if (!strcmp(devtype, "disk")) {
                    handleDiskAdded(dp, evt); //处理型号为disk的事件,当插入一个移动设备,不管有多少个分区,首先第一个事件的类型就是disk,收集设备的相关信息,比如分区总数mDiskNumParts
                } else {
                    handlePartitionAdded(dp, evt); //处理设备的分区事件信息
                }
                /* Send notification iff disk is ready (ie all partitions found) */
                if (getState() == Volume::State_Idle) { //通过上面的handle ,代表已经准备就绪
                    char msg[255];

                    snprintf(msg, sizeof(msg),
                             "Volume %s %s disk inserted (%d:%d)", getLabel(),
                             getMountpoint(), mDiskMajor, mDiskMinor);
                    mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted, //发送广播,调用的是SocketListener的sendBroadcast
                                                         msg, false);
                }
            }

         ...

       }

   }

}

在handlePartitionAdded中有收集分区的节点信息  mPartMinors[part_num -1] = minor; 并且设置Volume的状态到
Volume::State_Idle ,最后调用VolumeManager的实例发送广播

实际调用的是SocketListener的sendBroadcast,在Android—— 4.2 Vold挂载管理_CommandListener (二)中有分析!

void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
    pthread_mutex_lock(&mClientsLock); //加锁
    SocketClientCollection::iterator i;

    for (i = mClients->begin(); i != mClients->end(); ++i) { //遍历在SocketListener时创建的Client 依次发送广播
        // broadcasts are unsolicited and should not include a cmd number
        if ((*i)->sendMsg(code, msg, addErrno, false)) {
            SLOGW("Error sending broadcast (%s)", strerror(errno));
        }
    }
    pthread_mutex_unlock(&mClientsLock);
}

发送过程源码位于:/system/core/libsysutils/src/SocketClient.cpp中最终调用到:

int SocketClient::sendDataLocked(const void *data, int len) {
    int rc = 0;

    while (brtw > 0) {
        rc = send(mSocket, p, brtw, MSG_NOSIGNAL);

 ...

  }

}

这里的mSocket 为 "vold" 这个socket的表述符!

二.Volume处理:

在上面DirectVolume中发送出去广播之后,会到上层的MountService.java中,再通过"vold"socket 到Vold中,在SocketListener 中会接收到,依次会调用到FrameworkListener.cpp中的onDataAvailable,dispatchCommand

再到CommandListener.cpp中的runCommand,VolumeManager.cpp中的mountVolume  
Android—— 4.2 Vold挂载管理_CommandListener (二)中有分析!

int VolumeManager::mountVolume(const char *label) {
    Volume *v = lookupVolume(label);

    if (!v) {
        errno = ENOENT;
        return -1;
    }

    return v->mountVol(); //调用到Volume的mountVol
}

看Volume.cpp中:

int Volume::mountVol() {
    dev_t deviceNodes[4];
    int n, i, rc = 0;
    char errmsg[255];
    const char* externalStorage = getenv("EXTERNAL_STORAGE");
    bool primaryStorage = externalStorage && !strcmp(getMountpoint(), externalStorage);
    char decrypt_state[PROPERTY_VALUE_MAX];
    char crypto_state[PROPERTY_VALUE_MAX];
    char encrypt_progress[PROPERTY_VALUE_MAX];
    int flags;

    property_get("vold.decrypt", decrypt_state, "");
    property_get("vold.encrypt_progress", encrypt_progress, "");

    /* Don't try to mount the volumes if we have not yet entered the disk password
     * or are in the process of encrypting.
     */
    if ((getState() == Volume::State_NoMedia) ||
        ((!strcmp(decrypt_state, "1") || encrypt_progress[0]) && primaryStorage)) { //Volume的状态不对或者是加密的
        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) {// 正常的逻辑到这里的时候,该Volume的state应为State_Idle
        errno = EBUSY;
        if (getState() == Volume::State_Pending) {
            mRetryMount = true;
        }
        return -1;
    }
    ...

    for (i = 0; i < n; i++) {
        char devicePath[255];

        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);

        if (Fat::check(devicePath)) { //调用Fat的check,检查是否为Fat的文件系统
            if (errno == ENODATA) {
                SLOGW("%s does not contain a FAT filesystem\n", devicePath);
                continue;
            }

        ...

             if (primaryStorage) {//是否为指定的external storage 以不同的gid挂载
            // Special case the primary SD card.
            // For this we grant write access to the SDCARD_RW group.
            gid = AID_SDCARD_RW;
        } else {
            // For secondary external storage we keep things locked up.
            gid = AID_MEDIA_RW;
        }
        if (Fat::doMount(devicePath, "/mnt/secure/staging", false, false, false,  //这里就是整个Vold 里面真正的挂载
                AID_SYSTEM, gid, 0702, true)) {
            SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
            continue;
        }

     ...

     setState(Volume::State_Mounted);

     ...

  }

}

在整个挂载流程中state至关重要,通过state对挂载流程进行管控:

Volume.h中定义:

    static const int State_Init       = -1;
    static const int State_NoMedia    = 0;
    static const int State_Idle       = 1;
    static const int State_Pending    = 2;
    static const int State_Checking   = 3;
    static const int State_Mounted    = 4;
    static const int State_Unmounting = 5;
    static const int State_Formatting = 6;
    static const int State_Shared     = 7;
    static const int State_SharedMnt  = 8;

同时各个state的设置都向上层发送:

void Volume::setState(int state) {
    char msg[255];
    int oldState = mState;

    if (oldState == state) {
        SLOGW("Duplicate state (%d)\n", state);
        return;
    }

    if ((oldState == Volume::State_Pending) && (state != Volume::State_Idle)) {
        mRetryMount = false;
    }

    mState = state;

    SLOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel,
         oldState, stateToStr(oldState), mState, stateToStr(mState));
    snprintf(msg, sizeof(msg),
             "Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(),
             getMountpoint(), oldState, stateToStr(oldState), mState,
             stateToStr(mState));

    mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
                                         msg, false);
}

简单画了一张功能流程图:

DirectVolume/Volume的大体运作就是这样,后续分析与上层Framework层的交互。

时间: 2024-11-06 04:12:40

Android—— 4.2 Vold挂载管理_DirectVolume/Volume (五)的相关文章

Android—— 4.2 Vold挂载管理_MountService (六)

整个Vold机制应该算system层,与framwork层的交互在Android-- 4.2 Vold挂载管理_CommandListener (二)中有提到过,是通过一个"vold"的socket进行通信的,这里分析一下framework中负责与Vold通信的:MountService 撰写不易,转载请注明出处:http://blog.csdn.net/jscese/article/details/38978387 一.MountService启动: 在/frameworks/bas

Android—— 4.2 Vold挂载管理_mmcblk内置-双sdcard (八)

Vold的学习过了一段时间了,才想着把mmcblock内置成sdcard的方法记录一下. 当初就是因为要做这个功能才去学习Vold机制,贴出之前的博客: Android-- 4.2 Vold挂载管理_主体构建main (一) Android-- 4.2 Vold挂载管理_CommandListener (二) Android-- 4.2 Vold挂载管理_VolumeManager (三) Android-- 4.2 Vold挂载管理_NetlinkManager (四) Android-- 4

Android—— 4.2 Vold挂载管理_NetlinkManager (四)

在前文Android-- 4.2 Vold挂载管理_主体构建main (一)中有结构图表示,Vold是kernel与用户层的一个交互管理模块, Android-- 4.2 Vold挂载管理_VolumeManager (三)简单介绍了核心VolumeManager的构建,这篇分析从kernel进程沟通到VolumeManager进程的关键:NetlinkManager 撰写不易,转载请注明出处:http://blog.csdn.net/jscese/article/details/3858602

Android—— 4.2 Vold挂载管理_CommandListener (二)

在前一篇博客中介绍了个大体结构 Android-- 4.2 Vold挂载管理_主体构建 (一),按照代码的顺序结构来依次分析,这里来看看CommandListener这个类做了什么. 撰写不易,转载请注明出处:http://blog.csdn.net/jscese/article/details/38434263 一:CommandListener构造 在/system/vold/main.cpp的main函数中构建实例: cl = new CommandListener(); vm->setB

Android—— 4.2 Vold挂载管理_VolumeManager (三)

在Android-- 4.2 Vold挂载管理_主体构建main (一)中分析了大体的框架,这里分析一下核心VolumeManager,再次补上结构框架图如下: 可以看到VolumeManager就是整个Android 磁盘挂载Vold机制的核心调度,上下连接的中转站! 我从Vold main代码的顺序结构来一次分析,上一篇Android-- 4.2 Vold挂载管理_CommandListener (二)  中分析了与framework层交互的CommandListener的功能作用. 这里分

Android—— 4.2 Vold挂载管理_Kernel_USB_Uevent (七)

在前文Android-- 4.2 Vold挂载管理_NetlinkManager (四)中有解析到Vold 是从kernel中获取uevent事件,来获取device信息,其中是通过一个Netlink的套接字,目前整个Vold机制也分析完了, 上篇 Android-- 4.2 Vold挂载管理_MountService (六) 分析了机制中最上层的,这里分析一下最下层的kernel uevent事件的发送,以USB设备为例! 撰写不易,转载请注明出处:http://blog.csdn.net/j

Android 6.0 - 动态权限管理的解决方案(转)

转自:http://www.cnblogs.com/dubo-/p/6018262.html Android 6.0 - 动态权限管理的解决方案 转载请标注 Android 6.0版本(Api 23)推出了很多新的特性, 大幅提升了用户体验, 同时也为程序员带来新的负担. 动态权限管理就是这样, 一方面让用户更加容易的控制自己的隐私, 一方面需要重新适配应用权限. 时代总是不断发展, 程序总是以人为本, 让我们为应用添加动态权限管理吧! 这里提供了一个非常不错的解决方案, 提供源码, 项目可以直

挂载了Cinder Volume的实例无法动态迁移排错

现象:挂载了Cinder Volume的实例无法动态迁移 [[email protected] nova]# tail -f compute.log 2016-01-13 16:36:12.870 18762 ERROR nova.virt.libvirt.driver [-] [instance: 9d3e4665-801e-44bd-b93a-82951102cc22] Live Migration failure: unable to resolve '/var/lib/nova/mnt/

Android 仿今日头条频道管理(下)(GridView之间Item的移动和拖拽)

前言 上篇博客我们说到了今日头条频道管理的操作交互体验,我也介绍了2个GridView之间Item的相互移动.详情请參考:Android 仿今日头条频道管理(上)(GridView之间Item的移动和拖拽) 今天把相对照较复杂的gridView的拖拽也记录下.在開始之前我们事先要了解下Android的事件分发机制.网上这方面的资料也比較多.由于自己定义控件大部分要用到事件分发机制的知识. 实现思路 要实现Item的拖拽.事实上并非真正要去拖拽GridView的Item.而是使用WindowMan