USBAlsaManager 作为安卓系统中 USB 音频设备与 ALSA(Advanced Linux Sound Architecture)框架衔接的核心管理组件,负责 USB 音频设备的枚举、识别、状态管理、与音频服务交互等关键工作,是实现 USB 声卡、USB 麦克风、USB 耳机等外设音频功能的核心枢纽。
USBAlsaManager 隶属于安卓USB 服务框架 与音频框架的衔接模块,源码路径为frameworks/base/services/usb/java/com/android/server/usb/UsbAlsaManager.java,是 USB 主机模式下音频设备管理的核心组件,主要承担以下核心定位:
- 监听 USB 音频设备的热插拔事件(插入 / 拔出);
- 解析 USB 音频设备的描述符,识别设备的输入 / 输出能力;
- 对接内核 ALSA 子系统,扫描并映射 USB 设备对应的 ALSA 声卡节点;
- 管理 USB 音频设备的生命周期,完成设备的注册、激活、注销;
- 与 AudioService 交互,同步 USB 音频设备状态,实现音频路由切换。
UsbAlsaManager的核心关联类有:
- UsbHostManager:USB 主机管理服务,监听 USB 总线设备状态,触发 USBAlsaManager 的设备管理逻辑;
- AlsaCardsParser :ALSA 声卡解析工具,扫描/proc/asound/目录,获取系统中 ALSA 声卡的编号、名称、描述等信息;
- UsbAlsaDevice:USB 音频设备实体类,封装单个 USB 音频设备的 ALSA 声卡信息、输入输出能力、设备状态等;
- UsbDescriptorParser:USB 描述符解析器,解析 USB 设备的接口描述符,判断设备是否具备音频输入 / 输出功能;
- AudioService:安卓音频核心服务,接收 USBAlsaManager 的设备状态通知,更新音频设备列表与路由策略。
UsbService启动的时候会调用UsbHostManager的systemReady(),在此函数中会一个线程,调用native的方法等待USB主机事件。 当发生USB设备插拔事件时会回调UsbHostManager的usbDeviceAdded和usbDeviceRemoved方法,触发USBAlsaManager 的设备管理流程。
//UsbHostManager.java
public void systemReady() {
synchronized (mLock) {
// Create a thread to call into native code to wait for USB host events.
// This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
Runnable runnable = this::monitorUsbHostBus;
new Thread(null, runnable, "UsbService host thread").start();
}
}
//native 监测usb host插拔事件
static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, jobject thiz)
{
struct usb_host_context* context = usb_host_init();
if (!context) {
ALOGE("usb_host_init failed");
return;
}
// this will never return so it is safe to pass thiz directly
usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
}
本文将基于安卓14的源码进行USB音频设备的插拔分析。
usb音频设备插入流程
===》UsbHostManager.usbDeviceAdded
private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass,byte[] descriptors) {
//判断添加的设备是否在黑名单中
if (isDenyListed(deviceAddress)) {
return false;
}
if (isDenyListed(deviceClass, deviceSubclass)) {
return false;
}
UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors);
if (deviceClass == UsbConstants.USB_CLASS_PER_INTERFACE
&& !checkUsbInterfacesDenyListed(parser)) {
return false;
}
synchronized (mLock) {
if (mDevices.get(deviceAddress) != null) {
Slog.w(TAG, "device already on mDevices list: " + deviceAddress);
//TODO If this is the same peripheral as is being connected, replace
// it with the new connection.
return false;
}
UsbDevice.Builder newDeviceBuilder = parser.toAndroidUsbDeviceBuilder();
if (newDeviceBuilder == null) {
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE,
parser.getRawDescriptors());
} else {
UsbSerialReader serialNumberReader = new UsbSerialReader(mContext,
mPermissionManager, newDeviceBuilder.serialNumber);
UsbDevice newDevice = newDeviceBuilder.build(serialNumberReader);
serialNumberReader.setDevice(newDevice);
mDevices.put(deviceAddress, newDevice);
// It is fine to call this only for the current user as all broadcasts are
// sent to all profiles of the user and the dialogs should only show once.
ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
if (usbDeviceConnectionHandler == null) {
getCurrentUserSettings().deviceAttached(newDevice);
} else {
getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
usbDeviceConnectionHandler);
}
//调用UsbAlsaManager的usbDeviceAdded
mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
。。。。
}
。。。。
}
}
UsbHostManager.usbDeviceAdded函数中首先调用isDenyListed判断设备是否在黑名单中,过滤不兼容的音频设备,避免异常设备接入导致系统故障;然后通过 UsbDescriptorParser 解析 USB 设备的接口描述符;创建UsbDevice,将此新的设备添加到mDevices列表中,最后调用UsbAlsaManager的usbDeviceAdded方法。
===》UsbAlsaManager.usbDeviceAdded
void usbDeviceAdded(String deviceAddress, UsbDevice usbDevice,
UsbDescriptorParser parser) {
// Scan the Alsa File Space
mCardsParser.scan();
// Find the ALSA spec for this device address
AlsaCardsParser.AlsaCardRecord cardRec =
mCardsParser.findCardNumFor(deviceAddress);
if (cardRec == null) {
Slog.e(TAG, "usbDeviceAdded(): cannot find sound card for " + deviceAddress);
return;
}
//等待设备准备完成
waitForAlsaDevice(cardRec.getCardNum(), true /*isAdded*/);
// Add it to the devices list
//是否是音频输入设备 并根据VID PID判断是否在黑名单中
boolean hasInput = parser.hasInput()
&& !isDeviceDenylisted(usbDevice.getVendorId(), usbDevice.getProductId(),
USB_DENYLIST_INPUT);
//是否是音频输出设备 并根据VID PID判断是否在黑名单中
boolean hasOutput = parser.hasOutput()
&& !isDeviceDenylisted(usbDevice.getVendorId(), usbDevice.getProductId(),
USB_DENYLIST_OUTPUT);
if (hasInput || hasOutput) {
//是否是耳机设备
boolean isInputHeadset = parser.isInputHeadset();
boolean isOutputHeadset = parser.isOutputHeadset();
boolean isDock = parser.isDock();
if (mAudioService == null) {
Slog.e(TAG, "no AudioService");
return;
}
UsbAlsaDevice alsaDevice =
new UsbAlsaDevice(mAudioService, cardRec.getCardNum(), 0 /*device*/,
deviceAddress, hasOutput, hasInput,
isInputHeadset, isOutputHeadset, isDock);
alsaDevice.setDeviceNameAndDescription(
cardRec.getCardName(), cardRec.getCardDescription());
if (IS_MULTI_MODE) {//支持多外设模式
deselectCurrentDevice(alsaDevice.getInputDeviceType());
deselectCurrentDevice(alsaDevice.getOutputDeviceType());
} else {
// At single mode, the first device is the selected device.
if (!mAlsaDevices.isEmpty()) {
deselectAlsaDevice(mAlsaDevices.get(0));
}
}
//添加设备 将设备添加到mAlsaDevices列表中
addAlsaDevice(alsaDevice);
//选择alsa device
selectAlsaDevice(alsaDevice);
}
addMidiDevice(deviceAddress, usbDevice, parser, cardRec);
}
- 调用 AlsaCardsParser 的scan()方法,扫描内核/proc/asound/目录下的声卡节点,获取所有 ALSA 声卡信息
- mCardsParser.findCardNumFor(deviceAddress):根据 USB 设备地址,匹配对应的 ALSA 声卡编号,建立 USB 设备与 ALSA 声卡的一一对应关系
- parser.hasInput()/parser.hasOutput():通过 UsbDescriptorParser 解析 USB 设备的接口描述符,筛选出具备音频类接口(USB Audio Class)的设备,排除非音频 USB 设备;判断设备是否支持音频输入(麦克风)、音频输出(扬声器 / 耳机)
- isDeviceDenylisted:内置 USB 音频设备黑白名单机制,根据设备 VID/PID 过滤不兼容的音频设备,避免异常设备接入导致系统故障
- addAlsaDevice:将新插入的 USB 音频设备添加到设备列表;调用selectAlsaDevice()方法激活设备,设置为当前默认 USB 音频设备
- IS_MULTI_MODE:支持单 USB 音频设备模式(同一时间仅激活一个设备)和多设备模式,适配不同系统定制需求
===》AlsaCardsParser.scan()
作用:扫描/proc/asound/目录,解析 cards、devices 等文件,获取所有 ALSA 声卡信息,缓存到列表。
public int scan() {
mCardRecords = new ArrayList<AlsaCardRecord>();
//private static final String kAlsaFolderPath = "/proc/asound";
//private static final String kCardsFilePath = kAlsaFolderPath + "/cards";
File cardsFile = new File(kCardsFilePath);
try {
FileReader reader = new FileReader(cardsFile);
BufferedReader bufferedReader = new BufferedReader(reader);
String line = "";
while ((line = bufferedReader.readLine()) != null) {
AlsaCardRecord cardRecord = new AlsaCardRecord();
cardRecord.parse(line, 0);
line = bufferedReader.readLine();
if (line == null) {
break;
}
cardRecord.parse(line, 1);
// scan "usbbus" file
int cardNum = cardRecord.mCardNum;
String cardFolderPath = kAlsaFolderPath + "/card" + cardNum;
File usbbusFile = new File(cardFolderPath + "/usbbus");
if (usbbusFile.exists()) {
// read it in
FileReader usbbusReader = new FileReader(usbbusFile);
String deviceAddress = (new BufferedReader(usbbusReader)).readLine();
if (deviceAddress != null) {
cardRecord.setDeviceAddress(kDeviceAddressPrefix + deviceAddress);
}
usbbusReader.close();
}
//找到后 添加到mCardRecords
mCardRecords.add(cardRecord);
}
reader.close();
if (mCardRecords.size() > 0) {
mScanStatus = SCANSTATUS_SUCCESS;
} else {
mScanStatus = SCANSTATUS_EMPTY;
}
} catch (FileNotFoundException e) {
mScanStatus = SCANSTATUS_FAIL;
} catch (IOException e) {
mScanStatus = SCANSTATUS_FAIL;
}
return mScanStatus;
}
===》mCardsParser.findCardNumFor
作用:根据 USB 设备地址,查找对应的 ALSA 声卡编号,返回声卡记录对象。
public AlsaCardRecord findCardNumFor(String deviceAddress) {
//遍历所有的声卡
for(AlsaCardRecord cardRec : mCardRecords) {
//判断声卡是否是USB 且deviceAddress与需要查找的匹配
if (cardRec.isUsb() && cardRec.mUsbDeviceAddress.equals(deviceAddress)) {
return cardRec;
}
}
return null;
}
===》UsbAlsaManager.selectAlsaDevice
private synchronized void selectAlsaDevice(UsbAlsaDevice alsaDevice) {
....
//关键调用
alsaDevice.start();
}
usb音频设备拔出流程
===》UsbHostManager.usbDeviceRemoved
private void usbDeviceRemoved(String deviceAddress) {
synchronized (mLock) {
UsbDevice device = mDevices.remove(deviceAddress);
if (device != null) {
//关键调用
mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
mPermissionManager.usbDeviceRemoved(device);
。。。。。
}
}
}
===》UsbAlsaManager.usbDeviceRemoved
synchronized void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) {
// Audio
//从mAlsaDevices中删除设备
UsbAlsaDevice alsaDevice = removeAlsaDevice(deviceAddress);
Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice);
if (alsaDevice != null) {
//等待设备移除
waitForAlsaDevice(alsaDevice.getCardNum(), false /*isAdded*/);
deselectAlsaDevice(alsaDevice);
if (IS_MULTI_MODE) {//多设备模式
selectDefaultDevice(alsaDevice.getOutputDeviceType());
selectDefaultDevice(alsaDevice.getInputDeviceType());
} else {
// If there are any external devices left, select the latest attached one
if (!mAlsaDevices.isEmpty() && mAlsaDevices.get(0) != null) {
selectAlsaDevice(mAlsaDevices.get(0));
}
}
}
......
}
- removeAlsaDevice:从设备列表中移除对应的 UsbAlsaDevice 实例,注销 ALSA 声卡节点,释放设备资源
- 调用deselectAlsaDevice()注销设备,停止设备音频功能,释放 ALSA 声卡资源;
- 如果存在其他可用的usb音频设备,调用selectAlsaDevice重新选择
====》UsbAlsaManager.deselectAlsaDevice
private synchronized void deselectAlsaDevice(UsbAlsaDevice selectedDevice) {
selectedDevice.stop();
}
UsbAlsaDevice 核心函数
上述UsbAlsaManager插入或者拔出设备后,最后分别调用UsbAlsaDevice的start()和stop()函数。
==》start()
public synchronized void start() {
startOutput();
startInput();
}
public synchronized void startInput() {
startDevice(INPUT);
}
/** Start using this device as selected USB output device. */
public synchronized void startOutput() {
startDevice(OUTPUT);
}
private void startDevice(int direction) {
if (mIsSelected[direction]) {
return;
}
mIsSelected[direction] = true;
mState[direction] = 0;
startJackDetect();
//关键调用!!!!!!
updateWiredDeviceConnectionState(direction, true /*enable*/);
}
==》stop()
/** Stop using this device as the selected USB Audio Device. */
public synchronized void stop() {
stopOutput();
stopInput();
}
/** Stop using this device as the selected USB input device. */
public synchronized void stopInput() {
if (!mIsSelected[INPUT]) {
return;
}
if (!mIsSelected[OUTPUT]) {
// Stop jack detection when both input and output are stopped
stopJackDetect();
}
//关键调用!!!!!!!!!!!
updateInputWiredDeviceConnectionState(false /*enable*/);
mIsSelected[INPUT] = false;
}
/** Stop using this device as the selected USB output device. */
public synchronized void stopOutput() {
if (!mIsSelected[OUTPUT]) {
return;
}
if (!mIsSelected[INPUT]) {
// Stop jack detection when both input and output are stopped
stopJackDetect();
}
//关键调用!!!!!!!!!!!
updateOutputWiredDeviceConnectionState(false /*enable*/);
mIsSelected[OUTPUT] = false;
}
public synchronized boolean updateInputWiredDeviceConnectionState(boolean enable) {
return updateWiredDeviceConnectionState(INPUT, enable);
}
public synchronized boolean updateOutputWiredDeviceConnectionState(boolean enable) {
return updateWiredDeviceConnectionState(OUTPUT, enable);
}
start()和stop()最后都会调用updateWiredDeviceConnectionState函数:
private boolean updateWiredDeviceConnectionState(int direction, boolean enable) {
....
boolean connected = direction == INPUT ? isInputJackConnected() : isOutputJackConnected();
int state = (enable && connected) ? 1 : 0;
if (state != mState[direction]) {
mState[direction] = state;
AudioDeviceAttributes attributes = new AudioDeviceAttributes(
mDeviceType[direction], mAlsaCardDeviceString, mDeviceName);
try {
//关键调用
mAudioService.setWiredDeviceConnectionState(attributes, state, TAG);
} catch (RemoteException e) {
return false;
}
}
return true;
}
最后会调用AudioService的setWiredDeviceConnectionState,上报给AudioPolicyService设备断开/连接的状态,从而切换音频路由。
调试方法
日志打印
- 查看日志:adb logcat -s UsbHostManager UsbAlsaManager UsbAlsaDevice AlsaCardsParser,监控设备插拔、声卡扫描、状态同步流程。
ALSA 声卡节点查看
- 查看系统 ALSA 声卡:adb shell cat /proc/asound/cards,确认 USB 设备对应的声卡编号;
- 查看声卡详情:adb shell cat /proc/asound/cardX/pcm0p/info,获取声卡 PCM 信息。
USB 设备信息查看
- 查看已连接 USB 设备:adb shell lsusb,获取设备 VID/PID、设备地址;
音频设备状态查看
- 查看音频设备列表:adb shell dumpsys audio,确认 USB 音频设备是否已添加到系统音频设备列表。