第24章 USB调试:USB Gadget、MTP/PTP、ADB调试、充电问题

USB调试,这大概是Android工程师打交道最多的模块之一了。说实话,USB这块看似简单,但坑是真不少。我见过不少同事,明明代码逻辑没问题,就是连不上adb,或者MTP传输到一半就断了。今天咱们就把USB Gadget、MTP/PTP、ADB调试和充电问题这几个核心点彻底捋一遍。

24.1 USB Gadget:Android的“变形金刚”

先说说USB Gadget。这个概念可能有些朋友不太熟,但它的作用非常关键。说白了,USB Gadget就是让Android设备能“扮演”不同的USB角色。

比如你插上电脑,它可以是MTP设备(传文件),可以是ADB设备(调试),也可以是充电设备。这些角色切换,全靠Gadget框架在底层调度。

我习惯把Gadget理解成一个“多面手”。它基于Linux的USB Gadget框架,通过配置文件来决定当前设备“扮演”什么。核心文件在 /sys/kernel/config/usb_gadget/ 下面。

核心思路: 一个USB物理端口,通过Gadget配置,可以同时支持多个功能(比如ADB + MTP)。这叫“复合设备”(Composite Device)。

来看一个典型的配置流程:

# 创建一个gadget实例
mkdir /sys/kernel/config/usb_gadget/g1
cd /sys/kernel/config/usb_gadget/g1

# 设置VID/PID(厂商ID和产品ID)
echo 0x18d1 > idVendor
echo 0x4ee7 > idProduct

# 创建字符串描述
mkdir strings/0x409
echo "Google" > strings/0x409/manufacturer
echo "Pixel 6" > strings/0x409/product
echo "0123456789ABCDEF" > strings/0x409/serialnumber

# 创建配置
mkdir configs/c.1
mkdir configs/c.1/strings/0x409
echo "Config 1: ADB + MTP" > configs/c.1/strings/0x409/configuration

# 添加ADB功能
mkdir functions/ffs.adb
ln -s functions/ffs.adb configs/c.1/

# 添加MTP功能
mkdir functions/mtp.gs0
ln -s functions/mtp.gs0 configs/c.1/

# 绑定UDC(USB Device Controller)
echo "musb-hdrc.0.auto" > UDC

嗯,这里要注意:UDC的名字每个平台不一样。我遇到过好几次,明明配置都对了,就是绑定失败,最后发现是UDC名字写错了。可以用 ls /sys/class/udc/ 查看当前可用的UDC。

24.2 MTP/PTP:文件传输的“面子”和“里子”

MTP(Media Transfer Protocol)和PTP(Picture Transfer Protocol)是用户最直观感受到的USB功能。插上电脑,能看到手机里的照片、视频,靠的就是它们。

但说实话,MTP在Android上的实现一直有点“脆弱”。我曾在项目中遇到一个经典问题:MTP连接后,电脑上能看到设备,但点进去是空的。折腾了半天,发现是 MTP的存储路径配置错了

MTP的核心配置在 /system/etc/mtp/mtp.conf/vendor/etc/mtp/mtp.conf 里。它决定了哪些目录暴露给电脑。

# 典型的mtp.conf内容
# 每一行代表一个暴露的存储路径
/storage/emulated/0
/storage/XXXX-XXXX

如果路径不存在,或者权限不对,MTP就会“罢工”。我建议你在调试MTP问题时,先确认这几个点:

  • 存储路径是否存在:用 adb shell ls -l /storage/ 检查
  • SELinux上下文是否正确:MTP需要 mtp_devicefuse 上下文
  • FUSE文件系统是否挂载:Android 11+ 默认用FUSE模拟sdcard,MTP依赖它

小技巧: 如果MTP连上后传输大文件经常中断,可以试试关闭USB的“仅充电”模式,或者换一根质量好点的数据线。别笑,我遇到过好几次,最后发现是线的问题。

24.3 ADB调试:开发者的“生命线”

ADB调试,这个不用我多说了吧?每个Android工程师的必备技能。但ADB连不上、掉线、授权失败这些问题,你肯定也遇到过。

ADB的底层依赖USB Gadget的 FunctionFS 功能。它通过 /dev/usb-ffs/adb/ 这个文件系统和上层通信。

我总结了一个ADB调试的“三板斧”:

  1. 检查USB连接状态lsusb 看设备是否被识别
  2. 检查ADB服务状态adb devices 看是否列出设备
  3. 检查授权状态adb kill-server; adb start-server; adb devices 重新授权

如果还是连不上,那就得看内核日志了:

# 抓取USB相关日志
adb shell dmesg | grep -i usb
adb logcat -b all | grep -i adb

我曾经遇到一个奇葩问题:ADB能连上,但执行 adb shell 就卡死。查了半天,发现是 adbd 进程的权限被SELinux限制了。解决方案是在 adbd.te 里加上对应的allow规则。

警告: 不要在生产环境的user版本上开启ADB调试。我见过有人把eng版本的adbd直接推到user版本上,结果导致安全漏洞。ADB调试只应该在eng或userdebug版本上使用。

24.4 充电问题:不只是“充不进去”

充电问题,表面看是硬件的事,但很多时候是软件配置的锅。USB充电涉及 BC1.2协议(Battery Charging Specification),它决定了设备能从USB口获取多少电流。

Android的充电逻辑在 healthdcharger 服务里。核心是读取 /sys/class/power_supply/ 下的节点。

我习惯用这几个命令快速诊断充电问题:

# 查看电池状态
cat /sys/class/power_supply/battery/status
# 查看当前电流
cat /sys/class/power_supply/battery/current_now
# 查看USB类型
cat /sys/class/power_supply/usb/type

常见的充电问题有:

  • 充电慢:可能是USB类型识别为SDP(标准下行端口),电流只有500mA。检查D+/D-线是否短路
  • 不充电:可能是BC1.2协商失败,或者充电IC的寄存器配置不对
  • 充电断断续续:可能是接触不良,或者温控策略太激进

避坑指南: 我曾经在调试一款平板时,发现插上充电器后设备显示“充电中”,但电量一直掉。最后发现是 充电IC的输入电流限制 设得太低,只有100mA。设备耗电比充电快,当然充不进去。修改 dts 里的 input_current_limit 后解决。

24.5 知识体系总览

下面这张图,把USB调试的核心模块和它们之间的关系画出来了。你可以把它当作一个快速索引。

USB调试知识体系 USB Gadget 框架 MTP / PTP ADB 调试 充电管理 mtp.conf 路径配置 SELinux / FUSE 权限 FunctionFS / adbd 授权 / RSA 密钥 BC1.2 协议协商 power_supply 节点 核心:Gadget 配置 → 功能注册 → 权限/协议 → 用户态服务

这张图里,USB Gadget是“大脑”,它决定了设备能提供哪些功能。MTP、ADB、充电是三个主要“输出”。每个输出背后,都有对应的配置文件和内核驱动在支撑。

调试的时候,我习惯从Gadget层开始查。先确认Gadget是否注册成功,再看具体功能是否使能。这样一层层往下,问题通常都能定位到。

好了,USB调试这块内容不少,但核心就是这几个点。你只要把Gadget的配置逻辑搞明白,MTP的路径和权限搞清楚,ADB的授权和SELinux弄明白,充电的协议和节点摸清楚,大部分问题都能迎刃而解。


专注资料整理