安卓音频子系统之USBAlsaManager

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 主机模式下音频设备管理的核心组件,主要承担以下核心定位:

  1. 监听 USB 音频设备的热插拔事件(插入 / 拔出);
  2. 解析 USB 音频设备的描述符,识别设备的输入 / 输出能力;
  3. 对接内核 ALSA 子系统,扫描并映射 USB 设备对应的 ALSA 声卡节点;
  4. 管理 USB 音频设备的生命周期,完成设备的注册、激活、注销;
  5. 与 AudioService 交互,同步 USB 音频设备状态,实现音频路由切换。

UsbAlsaManager的核心关联类有:

  1. UsbHostManager:USB 主机管理服务,监听 USB 总线设备状态,触发 USBAlsaManager 的设备管理逻辑;
  2. AlsaCardsParser :ALSA 声卡解析工具,扫描/proc/asound/目录,获取系统中 ALSA 声卡的编号、名称、描述等信息;
  3. UsbAlsaDevice:USB 音频设备实体类,封装单个 USB 音频设备的 ALSA 声卡信息、输入输出能力、设备状态等;
  4. UsbDescriptorParser:USB 描述符解析器,解析 USB 设备的接口描述符,判断设备是否具备音频输入 / 输出功能;
  5. 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);
}
  1. 调用 AlsaCardsParser 的scan()方法,扫描内核/proc/asound/目录下的声卡节点,获取所有 ALSA 声卡信息
  2. mCardsParser.findCardNumFor(deviceAddress):根据 USB 设备地址,匹配对应的 ALSA 声卡编号,建立 USB 设备与 ALSA 声卡的一一对应关系
  3. parser.hasInput()/parser.hasOutput():通过 UsbDescriptorParser 解析 USB 设备的接口描述符,筛选出具备音频类接口(USB Audio Class)的设备,排除非音频 USB 设备;判断设备是否支持音频输入(麦克风)、音频输出(扬声器 / 耳机)
  4. isDeviceDenylisted:内置 USB 音频设备黑白名单机制,根据设备 VID/PID 过滤不兼容的音频设备,避免异常设备接入导致系统故障
  5. addAlsaDevice:将新插入的 USB 音频设备添加到设备列表;调用selectAlsaDevice()方法激活设备,设置为当前默认 USB 音频设备
  6. 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));
			}
		}
	}
	......
}
  1. removeAlsaDevice:从设备列表中移除对应的 UsbAlsaDevice 实例,注销 ALSA 声卡节点,释放设备资源
  2. 调用deselectAlsaDevice()注销设备,停止设备音频功能,释放 ALSA 声卡资源;
  3. 如果存在其他可用的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 音频设备是否已添加到系统音频设备列表。
相关推荐
AI2512242 小时前
2026年9款主流AI视频生成器功能评测
人工智能·音视频
2401_885885043 小时前
视频短信二次开发接口怎么做?视频短信API发送教程
音视频
KevinCyao3 小时前
安卓android视频短信接口怎么集成?AndroidStudio视频短信开发指南
android
2401_885885043 小时前
视频短信第三方接口好开发吗?全国三网覆盖能力的视频短信平台
音视频
Android出海3 小时前
安卓侧载强制24小时冷却,第三方APK直投买量面临停摆
android·google play·app出海·android出海·android侧载·谷歌开发者·android开发者
kerli3 小时前
Compose 组件:LazyColumn 核心参数与 key/contentType 详解
android·前端
hay_lee4 小时前
匿名屠榜,阿里认领:HappyHorse 1.0 如何重写AI视频生成规则?
人工智能·音视频
开发者如是说4 小时前
可能是最好用的多语言管理工具
android·前端·后端
流星雨在线4 小时前
[汇总]Android Framework相关
android·framework