云计算百科
云计算领域专业知识百科平台

第 39 章:USB、ADB 与 MTP

Android 的 USB 连接可同时服务三类完全不同的使用场景:开发者通过 ADB 调试应用、普通用户通过 MTP 传输照片、配件厂商通过 USB 主机模式接入游戏手柄。每一类场景都使用软件栈中独立的模块,该软件栈覆盖从用户空间 Java 服务,一直到 Linux 内核的 USB 小工具(gadget)与主机控制器驱动。本章追踪 USB 线路上每一字节数据的流向,依次经过 HAL 层、框架层服务,最终抵达应用层,全文会引用真实的 AOSP 源码路径。

39.1 USB 框架概述 

39.1.1 整体架构概览 

Android USB 子系统划分为 4 个垂直层级:公开 SDK API(UsbManager)、系统服务(UsbService 及其子管理器)、硬件抽象层(AIDL HAL 接口 IUsb、IUsbGadget)、Linux 内核 USB 子系统(gadget 驱动、主机控制器驱动、configfs、functionfs)。

39.1.2 核心组件 

组件
类型
源码路径
作用
 
 
 
 
UsbManager SDK API frameworks/base/core/java/android/hardware/usb/UsbManager.java 应用使用的公开 API        
UsbService 系统服务 frameworks/base/services/usb/…/UsbService.java 核心协调器        
UsbDeviceManager 内部管理器 frameworks/base/services/usb/…/UsbDeviceManager.java Gadget 模式状态机        
UsbHostManager 内部管理器 frameworks/base/services/usb/…/UsbHostManager.java 主机模式设备枚举        
UsbPortManager 内部管理器 frameworks/base/services/usb/…/UsbPortManager.java Type‑C 端口管理        
UsbPermissionManager 内部管理器 frameworks/base/services/usb/…/UsbPermissionManager.java 按用户跟踪权限        
IUsb AIDL HAL hardware/interfaces/usb/aidl/…/IUsb.aidl 端口状态、角色切换        
IUsbGadget AIDL HAL hardware/interfaces/usb/gadget/aidl/…/IUsbGadget.aidl Gadget 功能配置        
adbd 原生守护进程 packages/modules/adb/daemon/main.cpp ADB 守护进程        
MTP Native 原生库 frameworks/av/media/mtp/ MTP 协议实现 MTP Service Java 服务 packages/services/Mtp/ MTP 文档提供者

39.1.3 双模式架构:Gadget 模式与主机模式 

同一个 USB Type‑C 端口可工作在两种完全不同的模式,模式由 USB 供电协议协商得到的数据角色决定:

设备 / Gadget 模式(UFP):Android 设备作为外设接入主机(通常是 PC)。支持 MTP 文件传输、ADB 调试、PTP 照片传输、RNDIS 网络共享、MIDI、USB 配件 AOA。内核 USB gadget 框架(configfs)对外提供复合 USB 功能。

主机模式(DFP):Android 设备充当 USB 主机。会枚举接入的 USB 外设(键盘、鼠标、存储、音频设备),并通过 UsbManager API 向应用提供访问。

UsbPortManager 通过 IUsb HAL 监听端口状态变更,协调两种模式之间的切换。

39.1.4 UsbManager —— 公开 API

UsbManager(源码:frameworks/base/core/java/android/hardware/usb/UsbManager.java)带有@SystemService注解,是应用与 USB 交互的入口。它提供:

设备 (gadget) 模式操作:

  • 查询与设置当前 USB 功能(MTP、PTP 等)
  • 获取 USB 配件信息
  • 打开 USB 配件连接

主机模式操作:

  • 枚举已连接 USB 设备(getDeviceList())
  • 请求设备通信权限
  • 打开设备连接(openDevice())

功能常量定义可用的 gadget 配置:

// 摘自UsbManager.java —— 功能位掩码
public static final long FUNCTION_NONE = 0;
public static final long FUNCTION_MTP = GadgetFunction.MTP; // 1 << 2
public static final long FUNCTION_PTP = GadgetFunction.PTP; // 1 << 4
public static final long FUNCTION_RNDIS = GadgetFunction.RNDIS; // 1 << 5
public static final long FUNCTION_MIDI = GadgetFunction.MIDI; // 1 << 3
public static final long FUNCTION_ACCESSORY = GadgetFunction.ACCESSORY; // 1 << 1
public static final long FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE; // 1 << 6
public static final long FUNCTION_ADB = GadgetFunction.ADB; // 1
public static final long FUNCTION_NCM = GadgetFunction.NCM; // 1 << 10
public static final long FUNCTION_UVC = GadgetFunction.UVC; // 1 << 7

这些常量直接映射 AIDL 可序列化对象 GadgetFunction,定义位置:hardware/interfaces/usb/gadget/aidl/android/hardware/usb/gadget/GadgetFunction.aidl。

39.1.5 UsbService —— 核心协调器 

UsbService(源码:frameworks/base/services/usb/java/com/android/server/usb/UsbService.java)实现 IUsbManager,运行在 system_server 进程。它是所有 USB 操作的 Binder 服务端点,将工作委派给各个专用子管理器:

UsbAuthManager 仅在enableUsbHostAuthorization标记开启时才会实例化(参考 UsbService.java 中mAuthManager = new UsbAuthManager(…)附近代码);它对接独立进程 Rust 守护进程,详见 39.10 节。

服务生命周期遵循标准 SystemService 模式:

  • 构造阶段:system_server 启动时实例化 UsbService。
  • systemReady():触发所有子管理器初始化。UsbHostManager 启动原生线程监听/dev/bus/usb设备插拔事件。UsbPortManager 向 HAL 查询当前端口状态。
  • 运行阶段:处理来自应用的 Binder 调用,广播 USB 状态变更,按用户配置管理权限与设置。
  • 39.1.6 系统属性与 Sysfs 路径 

    UsbDeviceManager 通过多个内核接口与系统属性监控、控制 USB 状态:

    接口
    路径
    用途
     
     
     
     
     
     
    USB 状态 sysfs /sys/class/android_usb/android0/state 旧版 gadget 状态            
    USB 功能 sysfs /sys/class/android_usb/android0/functions 旧版功能配置            
    UDC 控制器 sys.usb.controller(系统属性) ConfigFS UDC 名称            
    USB 配置 persist.sys.usb.config(系统属性) 持久化 USB 配置 RNDIS 地址 /sys/class/android_usb/android0/f_rndis/ethaddr 网络共享 MAC 地址 MIDI ALSA /sys/class/android_usb/android0/f_midi/alsa MIDI 设备信息
    UEvent 匹配 DEVPATH=/devices/virtual/android_usb/android0 旧版状态变更            
    UEvent 匹配 SUBSYSTEM=udc 现代 UDC 状态变更            
    FunctionFS /dev/usb‑ffs/adb/ ADB FunctionFS 端点            

    39.2 UsbDeviceManager:Gadget 模式状态机 

    39.2.1 概述 

    UsbDeviceManager(源码:frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java)是 USB 框架中最复杂的组件。它管理 Android 设备作为 USB 外设的行为,处理功能切换(MTP、PTP、RNDIS、配件、MIDI、ADB)、线缆事件触发的状态迁移,同时协调锁屏状态、用户偏好、内核层 USB 配置。

    该类实现ActivityTaskManagerInternal.ScreenObserver,用于响应锁屏状态变更。关键点:MTP 访问用户存储数据要求设备屏幕处于解锁状态。

    39.2.2 架构 

    39.2.3 双 Handler 策略 

    UsbDeviceManager 在构造时选择两种具体 Handler 实现之一:

    // 摘自UsbDeviceManager构造函数
    if (mUsbGadgetHal == null) {
    // 初始化传统UsbHandler
    mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(),
    mContext, this, alsaManager, permissionManager);
    } else {
    // 初始化基于HAL的UsbHandler
    mHandler = new UsbHandlerHal(FgThread.get().getLooper(),
    mContext, this, alsaManager, permissionManager);
    }

    • UsbHandlerHal:现代设备使用,设备具备 IUsbGadget AIDL HAL。调用 HAL 的setCurrentUsbFunctions()请求配置变更,HAL 内部完成内核 ConfigFS 操作。
    • UsbHandlerLegacy:旧设备无 gadget HAL 时的回退方案。直接写 sysfs 文件与系统属性切换 USB 功能。

    39.2.4 基于消息的状态机 

    UsbHandler 通过 Android Handler 消息队列处理 USB 状态迁移。所有状态变更串行在前台线程执行,避免竞态条件。

    消息 ID
    常量
    触发来源
    0 MSG_UPDATE_STATE 内核上报连接 / 断开 / 配置完成
    1 MSG_ENABLE_ADB 开发者选项开关 ADB
    2 MSG_SET_CURRENT_FUNCTIONS 应用请求切换功能
    3 MSG_SYSTEM_READY system server 就绪
    4 MSG_BOOT_COMPLETED 开机完成广播
    5 MSG_USER_SWITCHED 切换当前用户
    6 MSG_UPDATE_USER_RESTRICTIONS 用户策略变更
    7 MSG_UPDATE_PORT_STATE Type‑C 端口状态变更
    8 MSG_ACCESSORY_MODE_ENTER_TIMEOUT 配件协商 10 秒超时
    9 MSG_UPDATE_CHARGING_STATE 电池充电状态变更
    10 MSG_UPDATE_HOST_STATE 主机模式设备插拔
    11 MSG_LOCALE_CHANGED 语言变更(更新通知)
    12 MSG_SET_SCREEN_UNLOCKED_FUNCTIONS 屏幕解锁时功能偏好
    13 MSG_UPDATE_SCREEN_LOCK 锁屏显示 / 隐藏
    14 MSG_SET_CHARGING_FUNCTIONS 切换至仅充电模式
    15 MSG_SET_FUNCTIONS_TIMEOUT 功能切换超时
    16 MSG_GET_CURRENT_USB_FUNCTIONS 查询当前 gadget 功能
    17 MSG_FUNCTION_SWITCH_TIMEOUT gadget 重新枚举超时
    18 MSG_GADGET_HAL_REGISTERED HAL 服务就绪
    19 MSG_RESET_USB_GADGET 重置 gadget 硬件
    20 MSG_ACCESSORY_HANDSHAKE_TIMEOUT AOA 握手超时
    21 MSG_INCREASE_SENDSTRING_COUNT 收到 AOA 字符串描述符
    22 MSG_UPDATE_USB_SPEED USB 速率协商完成
    23 MSG_UPDATE_HAL_VERSION HAL 版本信息更新
    24 MSG_USER_UNLOCKED_AFTER_BOOT 开机后首次解锁

    39.2.5 USB 状态迁移 

    内核通过 UEvent 消息上报 USB 状态变更。UsbUEventObserver 接收消息并转换为 Handler 消息。

    UsbHandler 的updateState()将内核状态字符串映射为内部状态:

    // 摘自UsbHandler.updateState()
    if (\”DISCONNECTED\”.equals(state)) {
    connected = 0; configured = 0;
    } else if (\”CONNECTED\”.equals(state)) {
    connected = 1; configured = 0;
    } else if (\”CONFIGURED\”.equals(state)) {
    connected = 1; configured = 1;
    }

    39.2.6 功能切换 

    用户或系统请求切换 USB 功能时,状态机执行多步流程:

    39.2.7 防抖与超时 

    功能切换会带来短暂 USB 断开。状态机会做防抖处理,避免虚假断开事件干扰切换流程。

    // UsbDeviceManager中防抖延时
    private static final int DEVICE_STATE_UPDATE_DELAY_EXT = 3000; // 3秒
    private static final int DEVICE_STATE_UPDATE_DELAY = 1000; // 1秒
    private static final int HOST_STATE_UPDATE_DELAY = 1000; // 1秒
    private static final int ACCESSORY_REQUEST_TIMEOUT = 10 * 1000; // 10秒
    private static final int ACCESSORY_HANDSHAKE_TIMEOUT = 10 * 1000; // 10秒

    调用resetUsbGadget()之后,通过mResetUsbGadgetDisableDebounce临时关闭防抖,保证 gadget 重置后的第一次断开事件立即处理。

    39.2.8 与锁屏交互 

    MTP 需要访问用户存储,锁屏时必须做保护。UsbDeviceManager 与锁屏模块协同工作:

  • 屏幕锁定(onKeyguardStateChanged(true)),Handler 收到 MSG_UPDATE_SCREEN_LOCK。
  • 如果 MTP 或 PTP 处于激活状态,Handler 切换为仅充电功能。
  • 用户偏好功能保存在 SharedPreferences,key 为usb‑screen‑unlocked‑config‑<userId>。
  • 屏幕解锁后,恢复之前保存的 USB 功能。
  • 39.2.9 接口黑名单 

    出于安全考虑,当设备作为 USB 主机时,部分 USB 接口类禁止应用访问:

    // 摘自UsbDeviceManager静态代码块
    sDenyInterfaces.add(UsbConstants.USB_CLASS_AUDIO);
    sDenyInterfaces.add(UsbConstants.USB_CLASS_COMM);
    sDenyInterfaces.add(UsbConstants.USB_CLASS_HID);
    sDenyInterfaces.add(UsbConstants.USB_CLASS_PRINTER);
    sDenyInterfaces.add(UsbConstants.USB_CLASS_MASS_STORAGE);
    sDenyInterfaces.add(UsbConstants.USB_CLASS_HUB);
    sDenyInterfaces.add(UsbConstants.USB_CLASS_CDC_DATA);
    sDenyInterfaces.add(UsbConstants.USB_CLASS_CSCID);
    sDenyInterfaces.add(UsbConstants.USB_CLASS_CONTENT_SEC);
    sDenyInterfaces.add(UsbConstants.USB_CLASS_VIDEO);
    sDenyInterfaces.add(UsbConstants.USB_CLASS_WIRELESS_CONTROLLER);

    39.2.10 MTP 服务绑定 

    MTP/PTP 功能激活时,Handler 绑定 MTP 服务:

    // UsbHandler常量
    protected static final String MTP_PACKAGE_NAME = \”com.android.mtp\”;
    protected static final String MTP_SERVICE_CLASS_NAME = \”com.android.mtp.MtpService\”;

    ServiceConnection 在 MTP 会话整个生命周期保持绑定;切换离开 MTP/PTP 时解除绑定。该绑定除了启动服务外还有关键作用:阻止 ActivityManager 冻结 MTP 进程。如果没有绑定,系统可能为回收资源冻结 MTP 服务进程,打断正在进行的 USB 传输。

    39.2.11 MIDI 功能探测 

    MIDI gadget 功能启用后,UsbDeviceManager 需要探测合成 MIDI 设备对应的 ALSA 卡号与设备号,有两种实现方式。

    现代方案(基于 sysfs 解析):

    // 遍历UDC控制器下sysfs目录
    File soundDir = new File(\”/sys/class/udc/\” + controllerName + \”/gadget/sound\”);
    File[] cardDirs = FileUtils.listFilesOrEmpty(soundDir,
    (dir, file) -> file.startsWith(\”card\”));
    File[] midis = FileUtils.listFilesOrEmpty(cardDirs[0],
    (dir, file) -> file.startsWith(\”midi\”));

    // 从\”midiC<card>D<device>\”解析卡号设备号
    Pattern pattern = Pattern.compile(\”midiC(\\\\d+)D(\\\\d+)\”);
    Matcher matcher = pattern.matcher(midis[0].getName());
    if (matcher.matches()) {
    mMidiCard = Integer.parseInt(matcher.group(1));
    mMidiDevice = Integer.parseInt(matcher.group(2));
    }

    传统方案(读取 ALSA 文件):

    // 读取旧版sysfs路径
    Scanner scanner = new Scanner(new File(MIDI_ALSA_PATH));
    mMidiCard = scanner.nextInt();
    mMidiDevice = scanner.nextInt();

    探测得到的卡号、设备号传给 UsbAlsaManager,向 Android MIDI 服务注册外设 MIDI 设备。

    39.2.12 USB 状态广播 

    Handler 向所有接收者广播 USB 状态变更:

    protected void updateUsbStateBroadcastIfNeeded(long functions) {
    Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
    intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
    | Intent.FLAG_RECEIVER_FOREGROUND);
    intent.putExtra(UsbManager.USB_CONNECTED, mConnected);
    intent.putExtra(UsbManager.USB_HOST_CONNECTED, mHostConnected);
    intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
    intent.putExtra(UsbManager.USB_DATA_UNLOCKED,
    isUsbTransferAllowed() && isUsbDataTransferActive(mCurrentFunctions));

    // 添加激活功能标记
    long remainingFunctions = functions;
    while (remainingFunctions != 0) {
    intent.putExtra(UsbManager.usbFunctionsToString(
    Long.highestOneBit(remainingFunctions)), true);
    remainingFunctions -= Long.highestOneBit(remainingFunctions);
    }

    // 仅状态真正变化才广播
    if (!isUsbStateChanged(intent)) return;
    sendStickyBroadcast(intent);
    }

    ACTION_USB_STATE是粘性广播:晚注册的接收者会立刻收到上一次广播状态。Intent 携带各个激活功能的布尔值,接收方可以单独检查某一个功能状态。

    39.2.13 用户限制执行 

    企业管控设备可通过UserManager.DISALLOW_USB_FILE_TRANSFER限制 USB 文件传输:

    protected boolean isUsbTransferAllowed() {
    UserManager userManager = (UserManager) mContext.getSystemService(
    Context.USER_SERVICE);
    return !userManager.hasUserRestriction(
    UserManager.DISALLOW_USB_FILE_TRANSFER);
    }

    该限制生效时:

    • MTP、PTP 功能被屏蔽
    • USB 通知显示 “仅充电”
    • 应用无法切换至数据传输功能

    39.2.14 AOA 配件握手跟踪 

    Handler 记录 AOA 握手过程详细时序信息,用于调试与统计:

    private long mAccessoryConnectionStartTime = 0L; // 收到GET_PROTOCOL时刻
    private int mSendStringCount = 0; // SEND_STRING uevent计数
    private boolean mStartAccessory = false; // 是否收到START

    // 广播握手详情用于调试
    private void broadcastUsbAccessoryHandshake() {
    Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_HANDSHAKE)
    .putExtra(UsbManager.EXTRA_ACCESSORY_UEVENT_TIME,
    mAccessoryConnectionStartTime)
    .putExtra(UsbManager.EXTRA_ACCESSORY_STRING_COUNT,
    mSendStringCount)
    .putExtra(UsbManager.EXTRA_ACCESSORY_START,
    mStartAccessory)
    .putExtra(UsbManager.EXTRA_ACCESSORY_HANDSHAKE_END,
    SystemClock.elapsedRealtime());
    sendStickyBroadcast(intent);
    }

    39.2.15 RNDIS 网络共享集成 

    RNDIS(USB 网络共享)激活时:

  • Handler 配置 RNDIS gadget 功能。
  • 基于ro.serialno生成本地管理 MAC 地址:
  • // 首字节设0x02,表示本地管理地址
    address[0] = 0x02;
    String serial = SystemProperties.get(\”ro.serialno\”, \”1234567890ABCDEF\”);
    // USB序列号异或填充剩余5字节
    for (int i = 0; i < serialLength; i++) {
    address[i % (ETH_ALEN – 1) + 1] ^= (int) serial.charAt(i);
    }

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 第 39 章:USB、ADB 与 MTP
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!