Android6.0 MountService和vold详解(二) vold的emulatedvolume

Android6.0vold除了通信部分改动不大,其他基本改动很大,那我们就从头开始分析一下吧。


一、vold初始化


先看下main函数中下面这段代码

    if (!(vm = VolumeManager::Instance())) {//new 了volumemanager,构造函数中就是一些成员变量初始化
        LOG(ERROR) << "Unable to create VolumeManager";
        exit(1);
    }

    if (!(nm = NetlinkManager::Instance())) {//new 了NetlinkManager,构造函数中就是一些成员变量初始化
        LOG(ERROR) << "Unable to create NetlinkManager";
        exit(1);
    }

    if (property_get_bool("vold.debug", false)) {
        vm->setDebug(true);
    }

    cl = new CommandListener();//新建一个CommandListener
    ccl = new CryptCommandListener();
    vm->setBroadcaster((SocketListener *) cl);//CommandListener也负责和MountService通信
    nm->setBroadcaster((SocketListener *) cl);

    if (vm->start()) {//VolumManager的start方法
        PLOG(ERROR) << "Unable to start VolumeManager";
        exit(1);
    }

下面我们主要看下VolumManager的start方法:

int VolumeManager::start() {
    // Always start from a clean slate by unmounting everything in
    // directories that we own, in case we crashed.
    unmountAll();//unmount所有的

    // Assume that we always have an emulated volume on internal
    // storage; the framework will decide if it should be mounted.
    CHECK(mInternalEmulated == nullptr);
    mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(
            new android::vold::EmulatedVolume("/data/media"));
    mInternalEmulated->create();

    return 0;
}

新建了一个EmulatedVolume,先看下构造函数

EmulatedVolume::EmulatedVolume(const std::string& rawPath) :
        VolumeBase(Type::kEmulated), mFusePid(0) {
    setId("emulated");
    mRawPath = rawPath;
    mLabel = "emulated";
}

把data/media赋给了mRawPath变量


二、通知MountService emulated volume建立

接下来我们再看下EmulatedVolume的create函数,因为EmulatedVolume没有create函数,我们就看VolumeBase::create

status_t VolumeBase::create() {
    CHECK(!mCreated);

    mCreated = true;
    status_t res = doCreate();
    notifyEvent(ResponseCode::VolumeCreated,
            StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str()));
    setState(State::kUnmounted);
    return res;
}

在create函数中给MountService发送VolumeCreated命令,然后将该volume设置成Unmounted状态

我们看下MountService的onEvent处理:

            case VoldResponseCode.VOLUME_CREATED: {
                final String id = cooked[1];
                final int type = Integer.parseInt(cooked[2]);
                final String diskId = TextUtils.nullIfEmpty(cooked[3]);
                final String partGuid = TextUtils.nullIfEmpty(cooked[4]);
                final DiskInfo disk = mDisks.get(diskId);
                final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid);
                mVolumes.put(id, vol);
                onVolumeCreatedLocked(vol);
                break;
            }

注意本来MountService就在addInternalVolume函数中,在mVolumes加了data目录的volume。和这个不同
我们再来看看onVolumeCreatedLocked函数

onVolumeCreatedLocked方法会发送H_VOLUME_MOUNT消息,我们就不详细说这个函数了。因为vold发送上来的type是emulated的,于是

vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;

来看看这个消息的处理吧:

                case H_VOLUME_MOUNT: {
                    final VolumeInfo vol = (VolumeInfo) msg.obj;
                    if (isMountDisallowed(vol)) {
                        Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy");
                        break;
                    }
                    try {
                        mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
                                vol.mountUserId);
                    } catch (NativeDaemonConnectorException ignored) {
                    }
                    break;
                }

处理是直接给vold发送的mount的命令。

下面我们看看VolumeCmd::runCommand下的这段代码是处理MountService发给来的mount命令的

    } else if (cmd == "mount" && argc > 2) {
        // mount [volId] [flags] [user]
        std::string id(argv[2]);
        auto vol = vm->findVolume(id);
        if (vol == nullptr) {
            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
        }

        int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
        userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;

        vol->setMountFlags(mountFlags);
        vol->setMountUserId(mountUserId);

        int res = vol->mount();
        if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {//如果是Primary的调用volumeManager的setprimary方法
            vm->setPrimary(vol);
        }
        return sendGenericOkFail(cli, res);

    } 


三、建立mnt/user下的软链接


int VolumeManager::setPrimary(const std::shared_ptr<android::vold::VolumeBase>& vol) {
    mPrimary = vol;
    for (userid_t userId : mStartedUsers) {
        linkPrimary(userId);
    }
    return 0;
}

遍历已经启动的user,再调用linkPrimary方法:

int VolumeManager::linkPrimary(userid_t userId) {
    std::string source(mPrimary->getPath());
    if (mPrimary->getType() == android::vold::VolumeBase::Type::kEmulated) {
        source = StringPrintf("%s/%d", source.c_str(), userId);
        fs_prepare_dir(source.c_str(), 0755, AID_ROOT, AID_ROOT);
    }

    std::string target(StringPrintf("/mnt/user/%d/primary", userId));
    if (TEMP_FAILURE_RETRY(unlink(target.c_str()))) {
        if (errno != ENOENT) {
            SLOGW("Failed to unlink %s: %s", target.c_str(), strerror(errno));
        }
    }
    LOG(DEBUG) << "Linking " << source << " to " << target;
    if (TEMP_FAILURE_RETRY(symlink(source.c_str(), target.c_str()))) {
        SLOGW("Failed to link %s to %s: %s", source.c_str(), target.c_str(),
                strerror(errno));
        return -errno;
    }
    return 0;
}
上面这段代码就是把storage/emulated/0/ 创建软链接到mnt/user/0/primary

四、挂载emulated的volume


我们来看下volumeBase的mout函数

status_t VolumeBase::mount() {
    if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
        LOG(WARNING) << getId() << " mount requires state unmounted or unmountable";
        return -EBUSY;
    }

    setState(State::kChecking);//设置状态,没设置一个状态都会往MountService发送
    status_t res = doMount();
    if (res == OK) {
        setState(State::kMounted);
    } else {
        setState(State::kUnmountable);
    }

    return res;
}

我们再来看看EmulatedVolume的doMount函数

status_t EmulatedVolume::doMount() {
    // We could have migrated storage to an adopted private volume, so always
    // call primary storage "emulated" to avoid media rescans.
    std::string label = mLabel;
    if (getMountFlags() & MountFlags::kPrimary) {
        label = "emulated";
    }

    mFuseDefault = StringPrintf("/mnt/runtime/default/%s", label.c_str());
    mFuseRead = StringPrintf("/mnt/runtime/read/%s", label.c_str());
    mFuseWrite = StringPrintf("/mnt/runtime/write/%s", label.c_str());

    setInternalPath(mRawPath);
    setPath(StringPrintf("/storage/%s", label.c_str()));

    if (fs_prepare_dir(mFuseDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||
            fs_prepare_dir(mFuseRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||
            fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT)) {
        PLOG(ERROR) << getId() << " failed to create mount points";
        return -errno;
    }

    dev_t before = GetDevice(mFuseWrite);

    if (!(mFusePid = fork())) {
        if (execl(kFusePath, kFusePath,
                "-u", "1023", // AID_MEDIA_RW
                "-g", "1023", // AID_MEDIA_RW
                "-m",
                "-w",
                mRawPath.c_str(),
                label.c_str(),
                NULL)) {
            PLOG(ERROR) << "Failed to exec";
        }

        LOG(ERROR) << "FUSE exiting";
        _exit(1);
    }

    if (mFusePid == -1) {
        PLOG(ERROR) << getId() << " failed to fork";
        return -errno;
    }

    while (before == GetDevice(mFuseWrite)) {
        LOG(VERBOSE) << "Waiting for FUSE to spin up...";
        usleep(50000); // 50ms
    }

    return OK;
}
这个函数中先设置了setInternalPath,setPath,这两个函数最终都会通知MountService,在下面两个地方进行处理

            case VoldResponseCode.VOLUME_PATH_CHANGED: {
                if (cooked.length != 3) break;
                final VolumeInfo vol = mVolumes.get(cooked[1]);
                if (vol != null) {
                    vol.path = cooked[2];
                }
                break;
            }
            case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: {
                if (cooked.length != 3) break;
                final VolumeInfo vol = mVolumes.get(cooked[1]);
                if (vol != null) {
                    vol.internalPath = cooked[2];
                }
                break;
            }

其实InternalPath就是一个内存存储真正的路径data/media,path就是storage/emulated,而我们再看init.rc中一段

    mount none /mnt/runtime/default /storage slave bind rec

将mnt/runtime/default挂载到storage上,

symlink /mnt/user/0/primary /mnt/runtime/default/self/primary
init.rc中还有上面一个软链接,也就是说storage/self/primary也是/mnt/user/0/primary 的一个软链接

而对storage/emulated是利用fuse文件系统,再去读取data/media的
mount结束后最后在mount函数中setState(State::kMounted);

然后在MountService做如下处理,把mVolumes新的状态保存下来,再调用onVolumeStateChangedLocked

            case VoldResponseCode.VOLUME_STATE_CHANGED: {
                if (cooked.length != 3) break;
                final VolumeInfo vol = mVolumes.get(cooked[1]);
                if (vol != null) {
                    final int oldState = vol.state;
                    final int newState = Integer.parseInt(cooked[2]);
                    vol.state = newState;
                    onVolumeStateChangedLocked(vol, oldState, newState);
                }
                break;
            }

onVolumeStateChangedLocked函数中会去调用mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);通知各个listener状态改变







  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值