问题回顾

之前处理了一个SDCard格式化的问题,最后定位到是底层的原因,让底层同事排查去了,底层同事排查完,没发现什么问题,最近这个问题又转到我的手里了,于是我又深入看了一下

流程梳理

之前已经梳理过了SDCard格式化的代码流程,这里只贴一个简单的流程图

我又发现了Android13的一个bug

在Settings -> Storage页面点击SDCard格式化,最终请求vold完成格式化。格式化完毕后,再直接访问UnixFileSystem去获取磁盘大小,得到的结果为0。

这里贴一下访问UnixFileSystem获取磁盘大小的核心代码,对上层开发有参考作用,位于libcore/ojluni/src/main/native/UnixFileSystem_md.c

#define statvfs64 statvfs
// Android-changed: Name changed because of added thread policy check
JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getSpace0(JNIEnv *env, jobject this,
                                      jobject file, jint t)
{
    jlong rv = 0L;
    WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
        struct statvfs64 fsstat;
        memset(&fsstat, 0, sizeof(fsstat));
        if (statvfs64(path, &fsstat) == 0) {
            switch(t) {
            case java_io_FileSystem_SPACE_TOTAL:
                rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
                               long_to_jlong(fsstat.f_blocks));
                break;
            case java_io_FileSystem_SPACE_FREE:
                rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
                               long_to_jlong(fsstat.f_bfree));
                break;
            case java_io_FileSystem_SPACE_USABLE:
                rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
                               long_to_jlong(fsstat.f_bavail));
                break;
            default:
                assert(0);
            }
        }
    } END_PLATFORM_STRING(env, path);
    return rv;
}

主要是通过statvfs结构体,定义如下:


struct statvfs {
    unsigned long f_bsize;   // 文件系统块大小
    unsigned long f_frsize;  // 文件系统片段大小
    fsblkcnt_t f_blocks;     // 文件系统总块数
    fsblkcnt_t f_bfree;      // 文件系统剩余块数
    fsblkcnt_t f_bavail;     // 文件系统可用块数
    fsfilcnt_t f_files;      // 文件系统节点总数
    fsfilcnt_t f_ffree;      // 文件系统剩余节点数
    fsfilcnt_t f_favail;     // 文件系统可用节点数
    unsigned long f_fsid;    // 文件系统标识
    unsigned long f_flag;    // 挂载标志
    unsigned long f_namemax; // 文件名最大长度
};

在使用 statvfs 函数获取文件系统信息时,可以通过访问 statvfs 结构体的成员来获取所需的信息。应用开发者在做文件系统相关需求时,可以考虑使用此API。

深入分析

因为问题复现后,重新切换一下SDCard或者退出重新进入StorageDashboardFragment可以恢复正常,于是,我在进入StorageDashboardFragment、切换SDCard、格式化完毕这三处代码埋下了log,打印当前SDCard的信息,发现了根本原因:

我又发现了Android13的一个bug

格式化前后,SDCard的路径竟然变了,而SDCard的路径是由SDCard的fsUuid组成的,fsUuid就是磁盘在文件系统中的uuid。正常情况,无论是拔插还是格式化,这个fsUuid应该是不变的。这个变动就导致格式化完之后,Settings拿着旧的路径去文件系统查找磁盘信息,结果找不到。而退出页面重新进入,相当于重新获取了磁盘信息,此时再去文件系统查找就没问题了。

解决方案

好了,上层问题根源定位到了,怎么解决这个问题呢?

  • 对于上层来说,很简单,在格式化完毕的代码处重新获取一下磁盘信息不就好了,或者干脆直接初始化StorageDashboardFragment。

  • 对于底层来说,似乎要去更改vold格式化的逻辑,使其fsUuid不随机分配。

显然这两种方案都略显粗暴,不够优雅,因为无论是上层Settings代码,还是底层vold代码,都是Google原生的代码,那么这个问题就很有可能是Android自身的bug。

惊天大坑

带着这个怀疑,我去查阅了AOSP的提交记录,果真被我找到了与此相关的一笔提交:android-review.googlesource.com/c/platform/…

我又发现了Android13的一个bug

并在提交里找到了该开发者提出的这个问题:issuetracker.google.com/issues/3713…

我又发现了Android13的一个bug

这简直就是一模一样的问题啊,只是这个问题现象是不能显示SDCard的内容,他是在Android7.0上出现的,我是Android13。并且这个哥们儿2017-03-02反馈的这个bug,2017-05-09提交了修改,但Google至今一直没有合入这笔修改,且表示不会修复这个问题

我又发现了Android13的一个bug

这也就意味着从Android7.0开始,以后每个版本都会有这个问题,坑爹呢这不是。

个人想法

这个bug从一月份,一直解到现在,持续了将近3个月,最终居然是Android的bug,不免让人有些欲哭无泪。不过冷静下来思考,这个问题确实影响也不大,开发者自己也能修复,Android bug那么多,Google那帮大佬们应该都忙着修复紧急bug去了吧。