背景
查看日志发现 U盘不识别的原因是在 doMount 时报错,错误码为 -5

根据错误码(-EIO)和日志定位到发生错误的位置,发现是在mount fuse的时候报的错误。到这步说明设备能被系统正确的识别到,也已经挂载了,只是 onVolumeChecking 这步发生了错误,随后就把设备卸载了

/android/system/vold/model/PublicVolume.cpp#343
Callback 的实际值是返回的 mMountCallback 这个类成员变量,这个变量并不是由Vold决定的,而是上层StorageManagerService通过setMountCallback 设置的


android/system/vold/VoldNativeService.cpp#280
而且 onVolumeChecking 这个函数也是个虚函数,因此我们需要知道 StorageManagerService 到底重写成了什么样的形式
从C++层的接口映射到 Framework 层的 java 方法的实现如下

上层重写这个函数后所做的主要工作是将卷的两个存储路径进行保存,以及执行mStorageSessionController.onVolumeMount(pfd, vol),重写之后对应的参数:
| C++ | JAVA | 含义 |
|---|---|---|
| unique_fd fuseFd | FileDescriptor fd | 文件描述符 |
| std::string path | String path | 卷的存储路径 |
| std::string internalPath | String internalPath | 卷的存储路径(内部) |
| bool* _aidl_return | boolean (直接返回值) | 返回 bool 指针 |
因为底层的vold是通过判断_aidl_return这个指针指向的值来判定上层check是否成功,所以我们只需要关注StorageManagerService 的mount函数是否存在异常即可,如果异常就会输出相应log后return false,再看 onVolumeMount 函数具体的实现

android/server/storage/StorageSessionController.java#109
根据log可以看出 connection.startSession(*) 之前的程序都是顺利执行的,理论上来说是走到正确的分支,然后return true,但实际上并没有,底层 vold 的判定仍然是 false ,才会返回-EIO的错误。但是 mount 函数有try...catch 处理,如果有异常情况是会打印对应log的,这个地方非常的费解!!!

方案
采用 onVolumeStateChanged 的方式进行监听,对应vold也可以弃用fuse 的挂载方式,将移动设备作为 portable 设备进行挂载,这样就不会引起上述问题。此外,弃用fuse,一方面可以去掉fuse引起的一些操作,如切换账号时对外部设备的remount,另一方面可以避免Framework卡死的概率性事件
从 fuse 挂载变成 portable 设备挂载具体内容参考:U盘fuse挂载变为portable设备挂载