系列目录 :第一篇:全景图与调用链路概览 | 第二篇:内核层---USB驱动与uevent | 第三篇:Native层---vold与NetlinkManager | 第四篇:Framework层(上)---UsbHostManager | 第五篇:Framework层(下)---StorageManagerService | 第六篇:广播分发与SystemUI响应 | 第七篇:应用层---MediaScanner与SAF | 第八篇:实战调试与案例分析
一、引言
前面七篇文章,我们沿着"从硬件到应用"的方向,逐层拆解了整条 U 盘插拔链路。然而在实际工作中,你很少需要关注整条链路------你需要的是 当一个具体环节出问题时,能快速定位到根因。
本文是系列收官之作,聚焦实战:从调试工具链到典型案例分析,帮助你在面对 "U 盘插入没反应" 这类问题时,能像剥洋葱一样一层层定位。
二、调试工具链
2.1 全线工具全景
┌─────────────────────────────────────────────────────────────┐
│ 调试层 工具 观察什么 │
├─────────────────────────────────────────────────────────────┤
│ 硬件层 示波器 / USB 分析仪 VBUS 电平、枚举波形 │
│ ─────────────────────────────────────────────────────────── │
│ 内核层 dmesg / sysfs USB 枚举日志、设备树 │
│ ─────────────────────────────────────────────────────────── │
│ Native 层 logcat -b events uevent 日志 │
│ /dev/socket/vold vold 通信 │
│ ─────────────────────────────────────────────────────────── │
│ Framework 层 logcat -s 服务回调日志 │
│ dumpsys usb/storage 快照服务状态 │
│ ─────────────────────────────────────────────────────────── │
│ 应用层 logcat -s MediaProvider MediaScanner 日志 │
│ Settings → 存储 UI 展示确认 │
└─────────────────────────────────────────────────────────────┘
2.2 dmesg ------ 内核层诊断第一入口
bash
# 插入 U 盘后立即抓取内核日志
adb shell dmesg | tail -50
# 实时监控内核日志(持续输出)
adb shell dmesg -w
# 只过滤 USB 相关日志
adb shell dmesg | grep -iE 'usb|scsi|sd[a-z]|xhci|dwc3'
正常插入 U 盘的 dmesg 输出示例(USB 3.0 U 盘):
[ 234.123456] usb 2-1: new SuperSpeed Gen 1 USB device number 3 using xhci-hcd
[ 234.145678] usb 2-1: New USB device found, idVendor=0781, idProduct=5591, bcdDevice= 1.00
[ 234.145690] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 234.145698] usb 2-1: Product: Ultra USB 3.0
[ 234.145705] usb 2-1: Manufacturer: SanDisk
[ 234.145712] usb 2-1: SerialNumber: 4C530001260121106363
[ 234.160234] usb-storage 2-1:1.0: USB Mass Storage device detected
[ 234.160456] scsi host0: usb-storage 2-1:1.0
[ 235.180123] scsi 0:0:0:0: Direct-Access SanDisk Ultra USB 3.0 1.00 PQ: 0 ANSI: 6
[ 235.181234] sd 0:0:0:0: [sda] 60088320 512-byte logical blocks: (30.8 GB/28.6 GiB)
[ 235.181567] sd 0:0:0:0: [sda] Write Protect is off
[ 235.181578] sd 0:0:0:0: [sda] Mode Sense: 43 00 00 00
[ 235.182345] sda: sda1
[ 235.184567] sd 0:0:0:0: [sda] Attached SCSI removable disk
关键信息解读:
| 日志行 | 含义 | 对应阶段 |
|---|---|---|
new SuperSpeed Gen 1 USB device |
USB 3.0 设备被检测到 | Hub 端口检测 |
idVendor=0781, idProduct=5591 |
识别为 SanDisk 设备 | 枚举完成 |
usb-storage ...: USB Mass Storage device detected |
usb-storage 驱动绑定成功 | 驱动匹配 |
scsi 0:0:0:0: Direct-Access |
SCSI 设备注册成功 | SCSI 层 |
sd 0:0:0:0: [sda] 60088320 512-byte... |
块设备创建,30.8GB | 块设备层 |
sda: sda1 |
分区 sda1 被识别 | 分区表解析 |
2.3 logcat ------ Framework 层诊断
bash
# 观察 vold 日志
adb logcat -s vold
# 观察 UsbHostManager 日志
adb logcat -s UsbHostManager
# 观察 StorageManagerService 日志
adb logcat -s StorageManagerService
# 观察 MediaScanner 日志
adb logcat -s MediaScanner
# 一键抓取全线日志
adb logcat -s vold UsbHostManager StorageManagerService MediaScanner SystemUI | tee usb_debug.log
正常插入 U 盘的 logcat 输出示例:
--------- beginning of main
D/UsbHostManager: usbDeviceAttached: /dev/bus/usb/001/002
D/UsbHostManager: device: SanDisk Ultra USB 3.0, vid=0x0781, pid=0x5591
I/UsbHostManager: Added device /dev/bus/usb/001/002 (SanDisk Ultra USB 3.0)
--------- vold ---------
D/vold: handleBlockEvent(): devpath=/devices/.../block/sda, devtype=disk
D/vold: handleDiskAdded(): /dev/block/vold/sda
D/vold: readPartitions(): /dev/block/vold/sda, major=8, minor=0
D/vold: sda1: partition found, type=0x0B (FAT32)
D/vold: PublicVolume created: public:8,1
--------- StorageManagerService ---------
D/StorageManagerService: onDiskCreated: disk:8,0 flags=[SD]
D/StorageManagerService: onVolumeCreated: public:8,1 type=PUBLIC
D/StorageManagerService: onVolumeStateChanged: public:8,1 UNMOUNTED -> CHECKING
D/StorageManagerService: onVolumeStateChanged: public:8,1 CHECKING -> MOUNTED
I/StorageManagerService: Volume public:8,1 mounted at /mnt/media_rw/XXXX
--------- SystemUI ---------
D/StorageNotification: onMediaMounted: /mnt/media_rw/XXXX
D/StorageNotification: Showing notification for public:8,1
--------- MediaProvider ---------
D/MediaScannerReceiver: action: MEDIA_MOUNTED, uri: file:///mnt/media_rw/XXXX
I/MediaScannerService: Starting scan of /mnt/media_rw/XXXX
I/MediaScanner: Scanned 42 files, 3 directories in 1.2s
D/MediaScannerService: Scan complete, stopping service.
2.4 dumpsys ------ 快照当前状态
bash
# 查看 USB 设备状态
adb shell dumpsys usb
# 查看存储卷状态
adb shell dumpsys diskstats
# 查看所有存储信息
adb shell dumpsys mount
# 查看当前挂载点
adb shell dumpsys storaged --list
dumpsys usb 输出示例:
USB Manager State:
USB Device State:
Current Functions: mtp,adb
Default Functions: mtp
USB Host State:
Connected Devices:
/dev/bus/usb/001/003:
SanDisk Ultra USB 3.0
Class: 0
Subclass: 0
Protocol: 0
Vendor ID: 0781
Product ID: 5591
Has 1 interfaces:
Interface 0:
Class: 8 (Mass Storage)
Subclass: 6 (SCSI)
Protocol: 80 (Bulk-Only)
Endpoints:
Endpoint 0: Address=0x81, Attributes=0x02 (Bulk/IN)
Endpoint 1: Address=0x02, Attributes=0x02 (Bulk/OUT)
2.5 sysfs ------ 内核态状态探针
bash
# 查看当前连接的 USB 设备
adb shell ls /sys/bus/usb/devices/
# 查看特定设备的信息
adb shell cat /sys/bus/usb/devices/2-1/idVendor
# → 0781
adb shell cat /sys/bus/usb/devices/2-1/idProduct
# → 5591
adb shell cat /sys/bus/usb/devices/2-1/product
# → Ultra USB 3.0
# 查看设备速度
adb shell cat /sys/bus/usb/devices/2-1/speed
# → 5000 (SuperSpeed / USB 3.0)
# → 480 (High Speed / USB 2.0)
# 查看块设备大小
adb shell cat /sys/block/sda/size
# → 60088320 (扇区数 × 512 = 字节数)
三、手动触发 uevent 与挂载
3.1 手动模拟 uevent
bash
# 模拟块设备插入
adb shell
echo "add" > /sys/block/sda/uevent
# 模拟分区插入
echo "add" > /sys/block/sda/sda1/uevent
# 模拟拔出
echo "remove" > /sys/block/sda/uevent
3.2 vold 命令行调试
bash
# 通过 vdc(Volume Daemon Command)与 vold 交互
adb shell vdc volume list
# 列出所有磁盘
adb shell vdc disk list
# 挂载指定卷
adb shell vdc volume mount public:8,1
# 卸载
adb shell vdc volume unmount public:8,1
四、典型案例分析
案例 1:U 盘插入后通知栏无反应
现象: 插入 U 盘,指示灯亮,但通知栏没有任何提示。
排查过程:
步骤 1:确认硬件层正常
┌─────────────────────────────────────────────────────────────┐
│ $ adb shell dmesg | grep -iE 'usb|sd[a-z]' │
│ │
│ [良好] 看到 "New USB device found" → 硬件层正常 │
│ [不良] 看不到 → 检查供电/接触/USB模式(可能处于 Device 模式) │
└─────────────────────────────────────────────────────────────┘
│
▼
步骤 2:确认块设备创建
┌─────────────────────────────────────────────────────────────┐
│ $ adb shell ls /dev/block/sd* │
│ │
│ [良好] 看到 sda, sda1 → 块设备创建正常 │
│ [不良] 只有 sda 没有 sda1 → 可能是无分区的整盘 FAT(exFAT), │
│ 或分区表异常。用 fdisk -l /dev/block/sda 检查 │
└─────────────────────────────────────────────────────────────┘
│
▼
步骤 3:确认 vold 是否处理
┌─────────────────────────────────────────────────────────────┐
│ $ adb logcat -s vold │
│ │
│ [良好] 看到 "handleDiskAdded" + "handlePartitionAdded" │
│ [不良] 看到 "handleDiskAdded" 但没有 "handlePartitionAdded" │
│ → 分区表解析失败,可能是 exFAT/NTFS 格式未被识别 │
│ [不良] 完全没有 vold 日志 → uevent 丢失或过滤掉了 │
└─────────────────────────────────────────────────────────────┘
│
▼
步骤 4:确认 StorageManagerService 状态
┌─────────────────────────────────────────────────────────────┐
│ $ adb shell dumpsys mount │
│ │
│ [良好] 看到 Volume public:8,1 状态为 MOUNTED │
│ [不良] 状态为 UNMOUNTABLE → 文件系统不支持(如 exFAT/NTFS) │
│ → AOSP 默认不支持 exFAT,需 OEM 自行集成驱动 │
│ [不良] 状态停留 CHECKING → fsck 卡住了 │
└─────────────────────────────────────────────────────────────┘
常见根因与修复:
| 根因 | 检测方法 | 修复 |
|---|---|---|
| U 盘文件系统是 exFAT | dmesg 中看到 "unknown filesystem type 'exfat'" |
集成 exfatprogs / 内核 exfat 驱动 |
| U 盘文件系统是 NTFS | dmesg 中看到 "unknown filesystem type 'ntfs'" |
集成 ntfs-3g 或内核 NTFS 驱动 |
| 分区表为非标准格式 | vold 日志显示 readPartitions() 返回错误 |
用 fdisk 重建标准 MBR/GPT |
| USB 模式处于 Device | dumpsys usb 显示 Current Functions 包含 mtp/ptp |
切换或配置 OTG 自动切换 |
| SELinux 权限不足 | dmesg 中看到 "avc: denied" |
修改 sepolicy |
| vold 未启动 | `ps -A | grep vold` 无结果 |
案例 2:U 盘插入后卡在"正在检查"(CHECKING)
现象: 通知栏显示 "正在检查 U 盘",长时间不变化,最终可能显示 "U 盘已损坏"。
排查过程:
bash
# 1. 确认 vold 状态
adb logcat -s vold | grep -i check
# [错误] "fsck timed out" 或 "fsck failed"
# 2. 手动检查文件系统
adb shell fsck_msdos /dev/block/vold/public:8,1
# 3. 如果 fsck 失败,可能是文件系统损坏
# → Windows 下用 chkdsk 修复,或直接格式化
# 4. 如果 fsck 成功但超时,增大超时阈值
# frameworks/native/cmds/vold/fs/Vfat.cpp
# static const int kFsckTimeout = 10; // 从 5 秒调整到 10 秒
vold fsck 超时源码:
cpp
// system/vold/fs/Vfat.cpp
static const int kFsckTimeout = 5; // ★ 默认 5 秒超时
int Check(const std::string& source, const std::string& target) {
// ...
// 运行 fsck_msdos
int rc = ForkExecvpTimeout(
argv, nullptr, sFsckUntrustedContext, kFsckTimeout);
if (rc != 0) {
LOG(ERROR) << "fsck failed with rc=" << rc;
return -1;
}
// ...
}
经验: 大容量 U 盘(≥64GB)的 FAT32 fsck 可能超过 5 秒。如果量产软件遇到此类问题,建议将
kFsckTimeout调整为 15-20 秒。
案例 3:U 盘反复断开重连
现象: U 盘插入后,通知栏频繁弹出 "U 盘已移除" → "正在检查 U 盘" → "U 盘已移除",循环不断。
排查过程:
bash
# 1. 观察 dmesg------是否有反复的 disconnect/connect
adb shell dmesg -w | grep -iE 'disconnect|reset|enumerate'
# 典型日志:
# usb 2-1: reset SuperSpeed Gen 1 USB device
# usb 2-1: USB disconnect, device number 3
# usb 2-1: new SuperSpeed Gen 1 USB device number 4
# ↑ 明显异常
# 2. 检查供电
adb shell cat /sys/bus/usb/devices/2-1/power/runtime_status
# → active / suspended
# 3. 检查是否有硬件层错误
adb shell dmesg | grep -i 'error\|fail\|-110\|-71'
常见根因:
| 根因 | 症状 | 修复 |
|---|---|---|
| 供电不足 | 设备枚举成功但读写失败,反复重置 | 使用带外部供电的 Hub;检查 Type-C 接口供电能力 |
| 信号完整性差 | 高速模式不稳定,降级到全速 | 缩短线缆;检查 PCB layout |
| U 盘固件 bug | 特定命令后设备无响应,触发超时重置 | 更换 U 盘品牌 / 更新固件 |
| DWC3 驱动兼容性 | 特定 SoC 与特定 U 盘主控不兼容 |