Android12 U盘插拔链路源码全解析(五):Framework层(下) StorageManagerService

系列目录第一篇:全景图与调用链路概览 | 第二篇:内核层---USB驱动与uevent | 第三篇:Native层---vold与NetlinkManager | 第四篇:Framework层(上)---UsbHostManager | 第五篇:Framework层(下)---StorageManagerService | 第六篇:广播分发与SystemUI响应 | 第七篇:应用层---MediaScanner与SAF | 第八篇:实战调试与案例分析


一、引言

上一篇文章我们拆解了 Framework 层的 USB 设备感知链(UsbHostManager)。本篇聚焦真正让 U 盘"可用"的角色------StorageManagerService

它的职责可以概括为两句话:

  1. 向下:通过 Binder 与 vold 通信,控制磁盘/卷的挂载、卸载、格式化
  2. 向上:向应用层广播存储状态变化,提供 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
相关推荐
林九生3 小时前
【实用技巧】MySQL 绿色版一键路径更新脚本详解 —— update_path.bat 深度解析
android·数据库·mysql
故渊at4 小时前
第十三板块:Android 综合架构与未来演进 | 第三十一篇:Android 架构演进与 Fuchsia OS 的挑战
android·架构·宏内核·微内核·fuchsia·ipc 性能博弈
aqi004 小时前
一文速览 HarmonyOS 6.1.1 推出的十个新特性
android·华为·harmonyos·鸿蒙·harmony
matrixmind14 小时前
aiomysql:异步场景下的 MySQL 驱动
android·数据库·mysql·其他
随遇丿而安4 小时前
第8周:弹窗 / 提示组件全功能与弹窗优化
android
zh_xuan4 小时前
诡异Bug:输入框删除字符,却越删越多
android·bug
nwsuaf_huasir4 小时前
matlab绘制尺寸和字体合适的图片插入到latex的方法
android·开发语言·matlab
future_li4 小时前
Speed Tools:一套低侵入的 Android 插件化 + 动态换肤 + 字体切换框架
android
杊页5 小时前
第一板块:Android 系统基石与运行原理 | 第二篇:Android 编译、打包与安装机制
android·操作系统