问题回顾
之前处理了一个SDCard格式化的问题,最后定位到是底层的原因,让底层同事排查去了,底层同事排查完,没发现什么问题,最近这个问题又转到我的手里了,于是我又深入看了一下
流程梳理
之前已经梳理过了SDCard格式化的代码流程,这里只贴一个简单的流程图
在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的信息,发现了根本原因:
格式化前后,SDCard的路径竟然变了,而SDCard的路径是由SDCard的fsUuid组成的,fsUuid就是磁盘在文件系统中的uuid。正常情况,无论是拔插还是格式化,这个fsUuid应该是不变的。这个变动就导致格式化完之后,Settings拿着旧的路径去文件系统查找磁盘信息,结果找不到。而退出页面重新进入,相当于重新获取了磁盘信息,此时再去文件系统查找就没问题了。
解决方案
好了,上层问题根源定位到了,怎么解决这个问题呢?
-
对于上层来说,很简单,在格式化完毕的代码处重新获取一下磁盘信息不就好了,或者干脆直接初始化StorageDashboardFragment。
-
对于底层来说,似乎要去更改vold格式化的逻辑,使其fsUuid不随机分配。
显然这两种方案都略显粗暴,不够优雅,因为无论是上层Settings代码,还是底层vold代码,都是Google原生的代码,那么这个问题就很有可能是Android自身的bug。
惊天大坑
带着这个怀疑,我去查阅了AOSP的提交记录,果真被我找到了与此相关的一笔提交:android-review.googlesource.com/c/platform/…
并在提交里找到了该开发者提出的这个问题:issuetracker.google.com/issues/3713…
这简直就是一模一样的问题啊,只是这个问题现象是不能显示SDCard的内容,他是在Android7.0上出现的,我是Android13。并且这个哥们儿2017-03-02反馈的这个bug,2017-05-09提交了修改,但Google至今一直没有合入这笔修改,且表示不会修复这个问题
这也就意味着从Android7.0开始,以后每个版本都会有这个问题,坑爹呢这不是。
个人想法
这个bug从一月份,一直解到现在,持续了将近3个月,最终居然是Android的bug,不免让人有些欲哭无泪。不过冷静下来思考,这个问题确实影响也不大,开发者自己也能修复,Android bug那么多,Google那帮大佬们应该都忙着修复紧急bug去了吧。