系列目录 :第一篇:全景图与调用链路概览 | 第二篇:内核层---USB驱动与uevent | 第三篇:Native层---vold与NetlinkManager | 第四篇:Framework层(上)---UsbHostManager | 第五篇:Framework层(下)---StorageManagerService | 第六篇:广播分发与SystemUI响应 | 第七篇:应用层---MediaScanner与SAF | 第八篇:实战调试与案例分析
一、引言
上一篇文章我们拆解了 Framework 层的 USB 设备感知链(UsbHostManager)。本篇聚焦真正让 U 盘"可用"的角色------StorageManagerService。
它的职责可以概括为两句话:
- 向下:通过 Binder 与 vold 通信,控制磁盘/卷的挂载、卸载、格式化
- 向上:向应用层广播存储状态变化,提供 StorageManager API
它是 vold(C++ Native)与 Android 应用层之间的 唯一桥梁。如果这个服务出问题,整个存储子系统就会"哑火"------vold 在底层忙活,应用层却什么也不知道。
二、启动链路
2.1 SystemServer 中的启动
源码路径 :frameworks/base/services/java/com/android/server/SystemServer.java
java
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
// ... 前面已启动 vold、UsbService 等 ...
t.traceBegin("StartStorageManagerService");
try {
// ★ 注意:先创建 StorageManagerService 实例,再通知系统就绪
mStorageManagerService = storageManagerService;
storageManagerService.start(); // ★ 注册到 ServiceManager
storageManagerService.systemReady(); // ★ 连接 vold
} catch (Throwable e) {
// ...
}
t.traceEnd();
}
启动顺序非常有讲究:
vold (Native daemon) ← init.rc 最早启动
↓
StorageManagerService ← SystemServer 中启动
↓ (systemReady)
connect to vold via Binder
↓
开始接收 vold 回调
2.2 构造函数
源码路径 :frameworks/base/services/core/java/com/android/server/StorageManagerService.java
java
class StorageManagerService extends IStorageManager.Stub
implements Watchdog.Monitor, ActivityManagerService.ScreenObserver {
public StorageManagerService(Context context) {
mContext = context;
mResolver = context.getContentResolver();
// ★ 四大 HandlerThread:各自运行独立的消息循环
// 1. VoldHandler:处理来自 vold 的回调
mVoldHandler = new HandlerThread("VoldHandler").start();
mVoldHandler = new StorageManagerServiceHandler(mVoldHandler.getLooper());
// 2. CallbacksHandler:回调 App 层的 StorageEventListener
mCallbacksHandler = new CallbacksHandler(mCallbacksHandlerThread.getLooper());
// 3. ObbHandler:处理 OBB (Opaque Binary Blob) 挂载
mObbHandler = new ObbHandler(mObbHandlerThread.getLooper());
// 4. AppOpsHandler:权限相关
mAppOpsHandler = new AppOpsHandler(mAppOpsHandlerThread.getLooper());
// 数据结构初始化
mDisks = new ArrayMap<>(); // key=diskId, value=DiskInfo
mVolumes = new ArrayMap<>(); // key=volId, value=VolumeInfo
mRecords = new ArrayMap<>(); // key=pkgName, value=StorageRecord
// 启动 AppOps 监听
mAppOps = (AppOpsService) ServiceManager.getService(Context.APP_OPS_SERVICE);
}
}
三、四大 HandlerThread 设计
StorageManagerService 使用了 4 个独立的 HandlerThread,这是一项经典的 Android 系统服务设计模式:
| HandlerThread | 职责 | 为什么独立 |
|---|---|---|
| VoldHandler | 处理来自 vold 的所有回调 | 避免 Binder 回调阻塞主线程,且 vold 回调可能很频繁 |
| CallbacksHandler | 回调 App 层的 StorageEventListener |
App 层回调可能很慢(IO、锁竞争),不能拖慢系统处理 |
| ObbHandler | OBB 文件挂载/卸载 | OBB 操作涉及大文件,耗时不可控 |
| AppOpsHandler | 权限变更响应 | 独立于存储核心逻辑 |
java
// ★ VoldHandler 中的回调处理
class StorageManagerServiceHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case H_VOLUME_MOUNT: {
// vold 报告:卷挂载成功
VolumeInfo vol = (VolumeInfo) msg.obj;
handleVolumeMount(vol);
break;
}
case H_DAEMON_CONNECTED: {
// vold 连接成功
handleDaemonConnected();
break;
}
case H_INTERNAL_BROADCAST: {
// 内部发送广播
sendInternalBroadcast(msg);
break;
}
// ... 更多 case
}
}
}
四、与 vold 的 Binder 通信
4.1 建立连接
java
public void systemReady() {
// ★ 连接到 vold daemon
mVold = IVold.Stub.asInterface(
ServiceManager.getService("vold"));
// 注册回调监听器(双向通信)
mVold.setListener(mVoldListener);
// ★ 重置所有已知的磁盘和卷状态
mVold.reset();
}
双向 Binder 架构:
StorageManagerService vold (Native)
─────────────────── ─────────────
IVold (同步调用)
│── mount(volId, ...) ──────────► VolumeManager::mountVolume()
│── unmount(volId) ──────────► VolumeManager::unmountVolume()
│── format(volId, ...) ──────────► VolumeManager::formatVolume()
│── fstrim(...) ──────────► ...
│── reset() ──────────► 重置所有状态
│
IVoldListener (异步回调)
◄── onDiskCreated() ◄────────── disk->create()
◄── onDiskDestroyed() ◄────────── disk->destroy()
◄── onVolumeCreated() ◄────────── vol->create()
◄── onVolumeStateChanged() ◄────── vol 状态变化
◄── onVolumeDestroyed() ◄─────── vol->destroy()
4.2 IVoldListener 回调实现
java
private final IVoldListener mVoldListener = new IVoldListener.Stub() {
@Override
public void onDiskCreated(String diskId, int flags) {
synchronized (mLock) {
DiskInfo disk = new DiskInfo(diskId, flags);
mDisks.put(diskId, disk);
// ★ 这里不直接发广播,等 volume 状态确认后再发
}
}
@Override
public void onDiskDestroyed(String diskId) {
synchronized (mLock) {
DiskInfo disk = mDisks.remove(diskId);
if (disk != null) {
// 清理关联的 volumes
// ...
}
}
}
@Override
public void onVolumeCreated(String volId, int type,
String diskId, String partGuid) {
synchronized (mLock) {
DiskInfo disk = mDisks.get(diskId);
VolumeInfo vol = new VolumeInfo(volId, type, disk, partGuid);
mVolumes.put(volId, vol);
// ★ 记录创建,等待后续 mount 状态变化
}
}
@Override
public void onVolumeStateChanged(String volId, int state) {
synchronized (mLock) {
VolumeInfo vol = mVolumes.get(volId);
if (vol != null) {
int oldState = vol.state;
vol.state = state;
// ★ 根据新旧状态决定下一步操作
mVoldHandler.obtainMessage(H_VOLUME_STATE_CHANGED,
oldState, state, vol).sendToTarget();
}
}
}
@Override
public void onVolumeDestroyed(String volId) {
synchronized (mLock) {
mVolumes.remove(volId);
}
}
};
五、Volume 状态机
5.1 状态枚举
源码路径 :frameworks/base/core/java/android/os/storage/VolumeInfo.java
java
public class VolumeInfo implements Parcelable {
// ★ 状态常量
public static final int STATE_UNMOUNTED = 0; // 未挂载(初始状态)
public static final int STATE_CHECKING = 1; // 正在检查文件系统
public static final int STATE_MOUNTED = 2; // ★ 已挂载(可用)
public static final int STATE_MOUNTED_RO = 3; // 只读挂载
public static final int STATE_BAD_REMOVAL = 5; // 异常拔出
public static final int STATE_UNMOUNTABLE = 6; // 无法挂载
public static final int STATE_EJECTING = 7; // 正在弹出
public static final int STATE_FORMATTING = 8; // 正在格式化
// ★ 类型常量
public static final int TYPE_PUBLIC = 0; // ★ 公共卷(U盘、SD卡)
public static final int TYPE_PRIVATE = 1; // 私有卷(adoptable storage)
public static final int TYPE_EMULATED = 2; // 模拟卷(内部存储)
public String id; // "public:179,1"
public int type;
public String diskId; // "disk:179,0"
public String partGuid;
public int mountFlags;
public int mountUserId = -1;
public int state; // ★ 当前状态
public String fsType; // "vfat", "exfat", "ntfs"
public String fsUuid;
public String fsLabel;
public String path; // ★ 挂载路径: "/mnt/media_rw/XXXX"
public String internalPath;
}
5.2 状态流转图
U盘插入
│
▼
┌────────────────┐
│ STATE_UNMOUNTED │ ←── 初始状态 / 卸载后
└───────┬────────┘
│ vold 检测到分区
▼
┌────────────────┐
│ STATE_CHECKING │ ←── fsck 检查文件系统
└───────┬────────┘
│ 检查通过
▼
┌────────────────┐ 拔出(正常/异常)
│ STATE_MOUNTED │ ───────────────┐
└───────┬────────┘ │
│ 用户点击"弹出" │
▼ ▼
┌────────────────┐ ┌──────────────────┐
│ STATE_EJECTING │ │ STATE_BAD_REMOVAL │
└───────┬────────┘ └──────────────────┘
│
▼
┌────────────────┐
│ STATE_UNMOUNTED │
└────────────────┘
5.3 状态驱动广播
java
private void handleVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
// ★ 状态到广播的映射
switch (newState) {
case VolumeInfo.STATE_UNMOUNTED:
sendBroadcast(Intent.ACTION_MEDIA_UNMOUNTED, vol);
break;
case VolumeInfo.STATE_CHECKING:
sendBroadcast(Intent.ACTION_MEDIA_CHECKING, vol);
break;
case VolumeInfo.STATE_MOUNTED:
case VolumeInfo.STATE_MOUNTED_RO:
sendBroadcast(Intent.ACTION_MEDIA_MOUNTED, vol); // ★ 最重要
break;
case VolumeInfo.STATE_BAD_REMOVAL:
sendBroadcast(Intent.ACTION_MEDIA_BAD_REMOVAL, vol);
break;
case VolumeInfo.STATE_EJECTING:
sendBroadcast(Intent.ACTION_MEDIA_EJECT, vol);
break;
}
}
六、挂载流程完整拆解
6.1 mountVolume() ------ 发起挂载请求
java
// ★ 这是 Java 层发起挂载的入口
public void mount(String volId) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
synchronized (mLock) {
VolumeInfo vol = mVolumes.get(volId);
if (vol == null) {
throw new IllegalArgumentException("No volume found for " + volId);
}
// ★ 调用 vold 挂载
try {
mVold.mount(volId, vol.mountFlags, vol.mountUserId);
} catch (Exception e) {
Slog.e(TAG, "Failed to mount " + volId, e);
}
}
}
6.2 vold 挂载成功后回调
java
// VoldHandler 中处理 vold 回调
case H_VOLUME_MOUNT: {
VolumeInfo vol = (VolumeInfo) msg.obj;
handleVolumeMount(vol);
break;
}
private void handleVolumeMount(VolumeInfo vol) {
if (DEBUG_VOLUME) Slog.d(TAG, "handleVolumeMount " + vol);
// 1. 发送 ACTION_MEDIA_MOUNTED 广播
Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.fromFile(new File(vol.path)));
intent.putExtra(EXTRA_VOLUME_ID, vol.id);
intent.putExtra(EXTRA_VOLUME_STATE, vol.state);
intent.putExtra(EXTRA_FS_TYPE, vol.fsType);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.WRITE_MEDIA_STORAGE);
// 2. 通知所有 StorageEventListener
mCallbacksHandler.obtainMessage(H_VOLUME_STATE_CHANGED,
oldState, newState, vol).sendToTarget();
}
6.3 完整挂载时序
StorageManagerService vold
──────────────────── ────
1. onVolumeCreated(volId, TYPE_PUBLIC, diskId, partGuid)
→ 创建 VolumeInfo,状态 = UNMOUNTED
2. onVolumeStateChanged(volId, CHECKING)
→ 发送 ACTION_MEDIA_CHECKING 广播
3. ★ 主动调用 mount(volId, flags, userId)
───────────────────────────────────────►
4. VolumeManager::mountVolume()
→ mount(2) 系统调用
→ 挂载到 /mnt/media_rw/XXXX
◄──────────────────────────────────────
5. onVolumeStateChanged(volId, MOUNTED)
→ ★ 发送 ACTION_MEDIA_MOUNTED 广播
→ 更新 vol.path = "/mnt/media_rw/XXXX"
→ 更新 vol.fsType = "vfat"
→ 回调 StorageEventListener
七、卸载流程
7.1 unmountVolume()
java
public void unmount(String volId) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
synchronized (mLock) {
VolumeInfo vol = mVolumes.get(volId);
if (vol == null) return;
try {
// ★ 先通过 vold 卸载
mVold.unmount(volId);
// ★ 发送 MEDIA_EJECT 广播
Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT,
Uri.fromFile(new File(vol.path)));
intent.putExtra(EXTRA_VOLUME_ID, vol.id);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
} catch (Exception e) {
Slog.e(TAG, "Failed to unmount " + volId, e);
}
}
}
7.2 异常拔出检测
java
// vold 回调:检测到设备异常拔出
@Override
public void onVolumeStateChanged(String volId, int state) {
if (state == VolumeInfo.STATE_BAD_REMOVAL) {
// ★ 发送 MEDIA_BAD_REMOVAL 广播
Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL,
Uri.fromFile(new File(vol.path)));
intent.putExtra(EXTRA_VOLUME_ID, volId);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
}
八、Application 层 API 暴露
StorageManagerService 通过 AIDL 暴露以下核心 API:
java
// frameworks/base/core/java/android/os/storage/IStorageManager.aidl
interface IStorageManager {
// ★ 挂载/卸载
void mount(String volId);
void unmount(String volId);
// ★ 格式化
void format(String volId);
// ★ 获取卷列表
VolumeInfo[] getVolumes(int flags);
// ★ 注册/取消监听器
void registerListener(IStorageEventListener listener);
void unregisterListener(IStorageEventListener listener);
// ★ 获取磁盘列表
DiskInfo[] getDisks();
// ★ 分区操作
void partitionPublic(String