Android磁盘之U盘卸载

Android  源码分析  2024年1月5日 am8:08发布11个月前更新 城堡大人
76 0 0

前言

之前介绍了U盘的挂载,那当然不能少了U盘的卸载。这里大概的记录一下,方便自己查阅。

Android P

这里很多都是网上的,我就走走流程。

正文

VolumeManager.cpp

handleBlockEvent()
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
    //申请一个lock
    std::lock_guard<std::mutex> lock(mLock);
    //在事件属性参数中查找设备的路径;
    std::string eventPath(evt->findParam("DEVPATH")?evt->findParam("DEVPATH"):"");
    //在事件属性参数中查找设备的类型;
    std::string devType(evt->findParam("DEVTYPE")?evt->findParam("DEVTYPE"):"");
    //判断是否是“disk”类型,磁盘设备,否的话,直接返回不做任何处理
    if (devType != "disk") return;
    //上报的数据中获取主设备号;
    int major = std::stoi(evt->findParam("MAJOR"));
    //上报的数据中获取次设备号;
    int minor = std::stoi(evt->findParam("MINOR"));
    //makedev()作用是将主次设备号联合得到具体可访问的设备对象索引;
    dev_t device = makedev(major, minor);
    //判断事件的动作类型
    switch (evt->getAction()) {
    case NetlinkEvent::Action::kAdd: {
        //循环判断mDiskSources,这个mDiskSources 在vold的main()函数时就已经配置好的。所以mDiskSources是固定的一些数据。 
        //在main()函数中调用了process_config()函数对VolumeManager::DiskSource进行赋值。具体是检索配置fstab文件,进行add操作。
        for (const auto& source : mDiskSources) {
            //从mDiskSources表获取的一个成员DiskSource类型的source,
            //调用该成员的matches函数对 第11行获取的设备路径eventPath,进行匹配字符串比较。
            if (source->matches(eventPath)) {
                // For now, assume that MMC and virtio-blk (the latter is
                // emulator-specific; see Disk.cpp for details) devices are SD,
                // and that everything else is USB
                int flags = source->getFlags();
                //判断主设备号是否是mmc的设备;
                if (major == kMajorBlockMmc
                    //判断是否是模拟器运行的;
                    || (android::vold::IsRunningInEmulator()
                    //判断主设备号必须在 block设备范围内
                    && major >= (int) kMajorBlockExperimentalMin
                    && major <= (int) kMajorBlockExperimentalMax)) {
                    //将该磁盘Disk的flag标志为kSd
                    flags |= android::vold::Disk::Flags::kSd;
                } else {
                    //将该磁盘Disk的flag标志为kUsb
                    flags |= android::vold::Disk::Flags::kUsb;
                }
                auto disk = new android::vold::Disk(eventPath, device,
                        source->getNickname(), flags);
                handleDiskAdded(std::shared_ptr<android::vold::Disk>(disk));
                break;
            }
        }
        break;
    }
    case NetlinkEvent::Action::kChange: {
        handleDiskChanged(device);
        break;
    }
    case NetlinkEvent::Action::kRemove: {
        handleDiskRemoved(device);
        break;
    }
    default: {
        break;
    }
    }
}

这里是event事件处理中心,主要处理disk类型事件。

以上注册都是摘抄,部分参考文章忘记附上了。

上面分别有三种状态

  1. kAdd 挂载

  2. kChange

  3. kRemove 卸载

我们这里分析U盘的卸载,跟一下流程。

handleDiskRemoved()
void VolumeManager::handleDiskRemoved(dev_t device) {
    //获取vm管理的mDisks表头
    //已经挂载成功的disk
    auto i = mDisks.begin();
    //判断mDisks表是否遍历结束;
    while (i != mDisks.end()) {
        //判断从mDisks表取得到的一个Disk的设备 与当前Uevent上报的设备是否一致;
        if ((*i)->getDevice() == device) {
            //判断设备比较一样后,将该Disk 从mDisks表里移除;
            (*i)->destroy();
            //删除释放到从mDisks表里移除的Disk;
            i = mDisks.erase(i);
        } else {
            ++i;
        }
    }
    //pending的disk 
    auto j = mPendingDisks.begin();
    while (j != mPendingDisks.end()) {
        if ((*j)->getDevice() == device) {
            j = mPendingDisks.erase(j);
        } else {
            ++j;
        }
    }
}

上面任务

  1. 从挂载的磁盘列表中移除当前磁盘,并执行destroy()

  2. 从Pending的磁盘列表中移除当前磁盘

我们主要关注第一个列表,已经挂载成功的列表中查询并destroy。

Disk.cpp

destroy()
status_t Disk::destroy() {
    CHECK(mCreated);
    //销毁所有volume
    destroyAllVolumes();
    mCreated = false;
    //通知StorageManagerService
    auto listener = VolumeManager::Instance()->getListener();
    if (listener) listener->onDiskDestroyed(getId());
    return OK;
}
destroyAllVolumes()
void Disk::destroyAllVolumes() {
    for (const auto& vol : mVolumes) {
        vol->destroy();
    }
    mVolumes.clear();
}

根据挂载流程中,mVolumes添加的是PublicVolume。

PublicVolume没有重写destroy(),找它父类VolumeBase。

VolumeBase.cpp

status_t VolumeBase::destroy() {
    CHECK(mCreated);
    //根据磁盘状态执行不同的需求,如果挂载成功了,就需要unmounted
    if (mState == State::kMounted) {
        unmount();
        setState(State::kBadRemoval);
    } else {
        setState(State::kRemoved);
    }
    auto listener = getListener();
    if (listener) listener->onVolumeDestroyed(getId());
    status_t res = doDestroy();
    mCreated = false;
    LOG(ERROR) << "Water_x VolumeBase  destroy 2";
    return res;
}

这里主要任务

  1. 判断磁盘状态,如果挂载就需要卸载,然后刷新状态

  2. 通知StorageManagerService磁盘卸载了

  3. 调用PublicVolume.doDestroy()

这里已知挂载成功过,也就是此时状态State::kMounted,因此需要unmount()

unmount()
status_t VolumeBase::unmount() {
    if (mState != State::kMounted) {
        return -EBUSY;
    }
    //设置正在退出状态
    setState(State::kEjecting);
    //不是太理解,上面已经变量过,难道考虑多分区?
    for (const auto& vol : mVolumes) {
        if (vol->destroy()) {
            LOG(WARNING) << getId() << " failed to destroy " << vol->getId()
                    << " stacked above";
        }
    }
    mVolumes.clear();
    //执行doUnmount()
    status_t res = doUnmount();
    setState(State::kUnmounted);
    return res;
}

对于for循环destroy(),不是很理解,这里猜测多分区或插入了Hub的考虑。具体我没有深入,这里不讨论哈。

最后走了doUnmount(),这个虚函数,子类实现了。

PublicVolume.cpp

doUnmount()
status_t PublicVolume::doUnmount() {
    //kill真正使用路径的进程
    KillProcessesUsingPath(getPath());
    //强制性卸载
    ForceUnmount(mFuseDefault);
    ForceUnmount(mFuseRead);
    ForceUnmount(mFuseWrite);
    ForceUnmount(mRawPath);
    if (mFusePid > 0) {
        kill(mFusePid, SIGTERM);
        TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0));
        mFusePid = 0;
    }
    rmdir(mFuseDefault.c_str());
    rmdir(mFuseRead.c_str());
    rmdir(mFuseWrite.c_str());
    rmdir(mRawPath.c_str());
    mFuseDefault.clear();
    mFuseRead.clear();
    mFuseWrite.clear();
    mRawPath.clear();
    return OK;
}

里面就懒得继续了跟了,就是卸载磁盘大致流程。

参考文章

 历史上的今天

  1. 2023: Android跳转WIFI界面的几种方式(0条评论)
  2. 2020: 王朔:上一秒痛苦,下一秒无聊(0条评论)
版权声明 1、 本站名称: 笔友城堡
2、 本站网址: https://www.biumall.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权,请留言

暂无评论

暂无评论...

随机推荐

ARGB 颜色取值与透明度对照表

什么是ARGBARGB 依次代表透明度(alpha)、红色(red)、绿色(green)、蓝色(blue)。Android颜色值格式Android中的颜色值一般格式是:#AARRGGBB。AA就是透明度值RRGGBB是颜色值例子如果UI设计师给的视觉稿标注是:颜色#FFFFFF,透明...

沈从文 : 独处

在我一个自传里,我曾经提到过水给我种种的印象。檐溜、小小的河流、汪洋万顷的大海,莫不对于我有过极大的帮助。我学会用小小脑子去思索一切,全亏得是水。我对于宇宙认识得深一点,也亏得是水。“孤独一点,在你缺少一切的时节,你就会发现,原来还有个你自己。”这是一句真话。我有我自己的生活与理想,可以说是...

Android app换肤简单记录

前言记录一下换肤的思路,方便自己查阅正文Android换肤已经烂大街了,很多大厂应用都做了,比如QQ、网易云、哔哩哔哩等,这些都是需要VVIP才可以体验的。这里推荐GitHub开源的而且很受欢迎的Android-skin-support,很强大。下面我就整理一下我对换肤的简单理解。换...

Android系统切换语言后,Activity中的文本没有改变

前言切换语言后,项目中的APP中的文本没有根据系统的语言改变而改变。这个是个小问题,但还是记录一下,方便自己查阅。正文隐藏内容!付费阅读后才能查看!¥1 ¥3多个隐藏块只需支付一次付费阅读参考文章《[摘]切换多国语言导致Fragment被回收,出现切换错乱》

Android状态栏之TimeView

前言状态栏上使用,TimeView为用于显示当前时间的View,支持24小时制和12小时制自动切换。记录于此,方便自己查阅。正文隐藏内容!付费阅读后才能查看!¥2 ¥3多个隐藏块只需支付一次付费阅读

Vim替换命令

前言简单记录Vim替换命令正文利用:s命令可实现字符串的替换# 命令之间是没有间隔的,下面表示为了看的清晰:范围 s /old /new如果没有指明范围,就表示当前行:s/old/new:s/old/new/g [g 表示当前行匹配的全部替换]:,$s/old/new/g ...