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
相关推荐
千里马学框架19 小时前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台20 小时前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone20 小时前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc21 小时前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077001 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone1 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen1 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone1 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui