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

本文深入解析了Android系统中Vold如何管理挂载操作,包括DirectVolume与Volume类的作用及其实现细节,并探讨了挂载流程中的状态管理。

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


                                                       撰写不易,转载请注明出处:http://blog.youkuaiyun.com/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层的交互。




906 D UserDataPreparer: Preparing user data; volumeUuid=null, userId=11, flags=0x1, isNewUser=true 03-13 11:13:58.432 root 248 248 D vold : fscrypt_prepare_user_storage for volume null, user 11, flags 1 03-13 11:13:58.432 root 248 248 D vold : Preparing: /data/system/users/11 03-13 11:13:58.432 root 248 248 D vold : Preparing: /data/misc/profiles/cur/11 03-13 11:13:58.433 root 248 248 D vold : Preparing: /data/system_de/11 03-13 11:13:58.433 1000 754 754 W HealthPermIntentTracker: Requested user handle: UserHandle{11} is not present in the state. 03-13 11:13:58.433 root 248 248 I vold : Verified that /data/system_de/11 has the encryption policy 5db7e42a9b00a8f566d9ca1088d53bcb v2 modes 9/9 flags 0x6 03-13 11:13:58.433 root 248 248 D vold : Preparing: /data/vendor_de/11 03-13 11:13:58.433 1010117 2654 2677 D MediaGrants: Removed 0 media_grants for 11 user for [com.android.car.settings, xzr.perfmon, com.android.keychain, com.android.providers.settings, com.google.android.car.garagemode.testapp, com.android.inputdevices, com.google.android.car.networking.preferenceupdater, com.android.localtransport, com.android.car.developeroptions, com.android.dynsystem, com.android.DeviceAsWebcam, com.android.server.telecom, com.android.car, android, com.android.location.fused]. Reason: Mode changed: android:read_external_storage 03-13 11:13:58.433 root 248 248 I vold : Verified that /data/vendor_de/11 has the encryption policy 5db7e42a9b00a8f566d9ca1088d53bcb v2 modes 9/9 flags 0x6 03-13 11:13:58.433 root 248 248 D vold : Preparing: /data/misc_de/11 03-13 11:13:58.433 root 248 248 I vold : Verified that /data/misc_de/11 has the encryption policy 5db7e42a9b00a8f566d9ca1088d53bcb v2 modes 9/9 flags 0x6 03-13 11:13:58.433 root 248 248 D vold : Preparing: /data/user_de/11 03-13 11:13:58.433 root 248 248 I vold : Verified that /data/user_de/11 has the encryption policy 5db7e42a9b00a8f566d9ca1088d53bcb v2 modes 9/9 flags 0x6 03-13 11:13:58.433 root 248 248 D vold : /system/bin/vold_prepare_subdirs 03-13 11:13:58.433 root 248 248 D vold : prepare 03-13 11:13:58.433 root 248 248 D vold : 03-13 11:13:58.433 root 248 248 D vold : 11 03-13 11:13:58.433 root 248 248 D vold : 1
03-28
<think>嗯,用户现在又提供了一段新的日志,需要我帮忙分析和解释。之前他们问了关于权限缺失的问题,现在看起来这次的问题可能与用户数据的准备和加密有关。 首先,我需要仔细看一下用户提供的日志内容。日志中有几个关键部分:UserDataPreparer在准备用户数据,vold进程在处理存储相关的操作,还有之前的HealthPermIntentTracker警告,以及MediaGrants相关的操作。 用户提到的UserHandle{11},这在之前的对话中也出现过。可能用户正在处理多用户环境下的问题,比如新建用户账户时出现的问题。之前的回答已经涉及到了用户句柄缺失的警告,这次可能是在用户创建过程中出现的更多细节。 接下来,日志中多次出现vold进程的操作,比如fscrypt_prepare_user_storage,准备各种目录如/data/system/users/11、/data/misc/profiles/cur/11等,并且验证了加密策略。这表明系统正在为新用户(用户ID 11)准备加密的存储空间。同时,MediaGrants部分提到了移除了0个媒体授权,可能是因为用户模式变化,比如权限变更。 用户可能需要了解这些日志的含义,尤其是为什么在准备用户数据时会出现HealthPermIntentTracker的警告,以及整个过程的正常性。他们可能关心这是否意味着用户创建过程中存在错误,或者是否有安全隐患,特别是加密相关的部分。 需要确认这些日志中的操作是否是正常流程的一部分。例如,当创建新用户时,系统需要为其准备数据目录并设置加密,vold负责这部分工作。而HealthPermIntentTracker的警告可能是在用户数据尚未完全初始化时,某些服务尝试访问用户句柄导致的,这可能是一个时序问题,但如果没有后续的错误,可能只是暂时的警告。 另外,MediaGrants显示移除了0个授权,这可能是因为新用户还没有任何媒体权限,所以移除操作没有实际影响。这可能也是正常现象,不需要特别处理。 用户可能还想知道这些日志中的加密策略验证是否成功,比如那些“Verified”的行是否表示一切正常。从日志看,vold成功验证了各个目录的加密策略,说明加密配置是正确的,没有错误。 综合来看,用户的日志显示系统正在正常创建新用户(用户11),准备其数据目录并应用加密策略,而HealthPermIntentTracker的警告可能是在用户尚未完全就绪时触发的,但后续步骤显示用户数据准备成功。用户可能需要确认是否在创建新用户时遇到问题,或者只是看到这些日志想确认其正常性。 此外,用户可能有更深层的需求,比如设备是否安全,或者多用户功能是否正常工作。需要建议他们检查用户创建后的状态,是否有功能异常,或者是否需要更新系统以修复潜在的兼容性问题。</think>以下是您提供的日志的逐层解析与解决方案: --- ### **日志核心流程** 该日志记录了Android系统为**新创建的用户(User 11)**准备数据存储空间的全过程,涉及**存储加密初始化**与**权限配置**,属于多用户设备(如平板、企业版手机)的典型操作。 --- ### **关键步骤分解** #### **1. 用户数据初始化** ```logcat 906 D UserDataPreparer: Preparing user data; volumeUuid=null, userId=11, flags=0x1, isNewUser=true ``` - **含义**:系统开始为用户11准备数据目录,`isNewUser=true`表明这是新用户首次创建。 - **关键参数**: - `flags=0x1`:对应`StorageManager.FLAG_STORAGE_DE`,表示准备**设备加密(Device Encryption)存储空间**。 - `volumeUuid=null`:操作主存储卷(通常是`/data`分区)。 --- #### **2. 存储加密准备(vold进程)** ```logcat D vold: fscrypt_prepare_user_storage for volume null, user 11, flags 1 D vold: Preparing: /data/system/users/11 ... I vold: Verified that /data/system_de/11 has the encryption policy 5db7e42a9b00a8f566d9ca1088d53bcb v2 modes 9/9 flags 0x6 ``` - **核心操作**: - **目录创建**:生成用户专属目录(如`/data/system/users/11`用于系统配置,`/data/user_de/11`用于加密应用数据)。 - **加密策略验证**:使用`fscrypt`(文件系统加密工具)确认目录加密策略一致,其中: - `v2 modes 9/9`:加密算法为AES-256-XTS(模式9)。 - `flags 0x6`:表示目录需要加密且允许递归加密子目录。 - **技术背景**:Android从7.0(API 24)起支持基于文件的加密(FBE),取代全盘加密(FDE)。 --- #### **3. 权限警告复现** ```logcat W HealthPermIntentTracker: Requested user handle: UserHandle{11} is not present in the state. ``` - **关联性**:此警告与[前次日志](链接)中的问题一致,表明**用户11的上下文尚未完全加载**。 - **时序解释**:由于用户数据仍在初始化中,部分服务可能提前尝试访问该用户导致警告,通常会在初始化完成后自动恢复。 --- #### **4. 媒体权限清理** ```logcat D MediaGrants: Removed 0 media_grants for 11 user... Reason: Mode changed: android:read_external_storage ``` - **含义**:系统清理用户11的媒体文件访问权限(如相册、音频),但因新用户无历史授权记录(移除数量为0),操作无实际影响。 - **触发条件**:用户存储加密状态变化(如首次创建或密钥轮换)。 --- ### **潜在问题排查** #### **场景1:用户创建失败** - **表现**:日志停留在`UserDataPreparer`或`vold`步骤,后续无成功记录。 - **解决方案**: 1. 检查存储空间是否充足:`adb shell df /data` 2. 确认文件系统无损坏:`adb shell e2fsck -f /dev/block/by-name/userdata` 3. 查看加密策略兼容性:`adb shell getprop ro.crypto.type` #### **场景2:加密策略验证失败** - **表现**:`vold`日志中出现`Failed to verify encryption policy`错误。 - **解决方案**: 1. 重启进入Recovery模式清除`/data`分区(**警告:导致数据丢失**)。 2. 更新系统版本修复加密驱动问题。 --- ### **技术总结** - **流程正常性**:此日志表明用户11已**成功创建**且存储加密正常,警告信息多为初始化时序问题,无需干预。 - **调试建议**: ```bash # 监控用户创建全流程 adb logcat -s UserDataPreparer:vold:I HealthPermIntentTracker:W # 查看用户11的加密状态 adb shell ls -lZ /data/user_de/11 ``` - **延伸阅读**:[Android多用户管理官方文档](https://source.android.com/docs/core/config/users) | [FBE加密实现细节](https://source.android.com/docs/security/features/encryption/file-based)
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值