Android Framework 音频子系统(02)音频系统框架
AOSP 音频架构
查看音频硬件命令
WIFBY:/ $ cat proc/asound/cards
0 [sun50iw10codec ]: sun50iw10-codec - sun50iw10-codec
sun50iw10-codec
1 [CAMERA ]: USB-Audio - WN Full HD CAMERA
WN-ZW-220419 WN Full HD CAMERA at usb-sunxi-ehci-1.2, high speed
设备索引 | 设备名称 | 设备类型 | 连接方式 | 备注 |
---|---|---|---|---|
0 | sun50iw10-codec | 板载音频设备 | SoC 内置 | 可能是扬声器或 3.5mm 接口 |
1 | WN Full HD CAMERA | USB 音频设备 | USB(usb-sunxi-ehci-1.2) | 摄像头自带麦克风 |
A133 音频框架
在 A133 中,存在 7 个音频设备,分别是:
Linux ALSA
ALSA(Advanced Linux Sound Architecture,高级 Linux 声音架构) 是 Linux 内核中的一套音频驱动框架,用于管理音频设备(声卡、麦克风、USB 音频设备等
ASoC(ALSA System on Chip) 是 ALSA(Linux 声音架构)专门为 SoC 设计的音频子系统,用于管理嵌入式系统(如开发板、智能设备)上的音频设备。
🔹 为什么需要 ASoC?
普通 PC 的声卡一般是标准化的(如 Intel HDA、USB 声卡),ALSA 可以直接支持。但嵌入式系统的音频硬件千差万别,需要更灵活的架构来管理。因此,ASoC 主要解决以下问题:
- 不同 SoC 芯片的音频控制逻辑不同(如 Allwinner、Rockchip、Qualcomm、Amlogic)
- 音频编解码器(Codec)种类繁多(不同厂商使用不同的 DAC/ADC 芯片)
- 开发板的音频架构复杂(可能有多个 I2S 接口、多个音频通路)
- 降低驱动代码重复,方便移植和维护
🔹 ASoC 结构 ASoC 把 SoC 的音频系统拆分成三大部分,这样更易于适配不同硬件:
组件 | 作用 | 例子 |
---|---|---|
Codec 驱动 | 负责管理 音频编解码芯片(DAC/ADC) | WM8960、ES8316、RT5645 |
Platform 驱动 | 负责 SoC 的 I2S、DMA 传输 | Allwinner ASoC、Rockchip I2S |
Machine 驱动 | 把 Codec 和 Platform 连接起来,定义音频路由 | 开发板的音频拓扑,比如扬声器连哪个接口 |
A133 音频接口对比
接口 | 用途 | 特点 | 适用场景 |
---|---|---|---|
I2S | 外部 Codec / DSP | 支持 192kHz 采样率、TDM、多通道传输 | 高质量音频播放、HiFi 设备 |
PCM | 语音通讯 | 兼容 VoIP / 蓝牙语音,支持短帧/长帧模式 | 蓝牙音频、对讲系统 |
DMIC | 数字麦克风接口 | 双通道 PDM,直连 MEMS 数字麦克风 | AI 语音助手、降噪拾音 |
AUDIO CODEC | 内部音频编解码器 | 内置 DAC/ADC,直驱耳机/扬声器 | 基础音频播放、录音 |
音频系统底层设备节点
WIFBY:/ $ ls dev/snd/
controlC0 controlC1 pcmC0D0c pcmC0D0p pcmC1D0c timer
WIFBY:/ $ cat /proc/asound/pcm
00-00: SUNXI-CODEC sun50iw10codec-0 : : playback 1 : capture 1
01-00: USB Audio : USB Audio : capture 1
设备文件 | 含义 |
---|---|
controlC0 |
控制设备 0(用于控制音频设备,如 mixer 设置) |
controlC1 |
控制设备 1(如果有多个声卡,每个声卡有自己的 control 设备) |
pcmC0D0c |
PCM 设备(Card 0, Device 0, Capture),表示音频输入(录音) |
pcmC0D0p |
PCM 设备(Card 0, Device 0, Playback),表示音频输出(播放) |
pcmC1D0c |
PCM 设备(Card 1, Device 0, Capture),表示第二张声卡的录音设备 |
timer |
音频时间同步相关设备 |
dumpsys media.audio_policy
RK3568识别到了USB-Audio - WN Full HD CAMERA
全志A133没有识别到
- Available input devices:
Device 3:
- id: 9
- tag name: USB-Audio - WN Full HD CAMERA
- type: AUDIO_DEVICE_IN_USB_DEVICE
- supported encapsulation modes: 0 - supported encapsulation metadata types: 0 - address: card=2;device=0;
- name: USB-Audio - WN Full HD CAMERA
- Profiles:
Profile 0:[dynamic format]
- format: AUDIO_FORMAT_PCM_16_BIT
- sampling rates:8000, 16000, 44100, 48000
- channel masks:0x000c, 0x0010, 0x80000001
Device 4:
- id: 6
- tag name: Built-In Mic
- type: AUDIO_DEVICE_IN_BUILTIN_MIC
- supported encapsulation modes: 0 - supported encapsulation metadata types: 0 - address: bottom
- Profiles:
Profile 0:[dynamic format][dynamic channels][dynamic rates]
Profile 1:
- format: AUDIO_FORMAT_PCM_16_BIT
- sampling rates:8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
- channel masks:0x000c, 0x0010
分析
frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
: AudioPolicyManager(clientInterface, false /*forTesting*/)
{
loadConfig();
initialize();
}
void AudioPolicyManager::loadConfig() {
if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {
ALOGE("could not load audio policy configuration file, setting defaults");
getConfig().setDefault();
}
}
status_t AudioPolicyManager::initialize() {
// -----------------------------------------
// mAvailableOutputDevices and mAvailableInputDevices now contain all attached devices
// open all output streams needed to access attached devices
for (const auto& hwModule : mHwModulesAll) {
ALOGI("Processing hardware module: %s", hwModule->getName()); // 打印正在处理的硬件模块名称
// 打印当前硬件模块的可用输出和输入设备信息
ALOGI("Available input devices for module %s: %s", hwModule->getName(), mAvailableInputDevices.toString().c_str());
ALOGI("Available output devices for module %s: %s", hwModule->getName(), mAvailableOutputDevices.toString().c_str());
hwModule->setHandle(mpClientInterface->loadHwModule(hwModule->getName()));
if (hwModule->getHandle() == AUDIO_MODULE_HANDLE_NONE) {
ALOGW("could not open HW module %s", hwModule->getName());
continue;
}
mHwModules.push_back(hwModule);
// open all output streams needed to access attached devices
// except for direct output streams that are only opened when they are actually
// required by an app.
// This also validates mAvailableOutputDevices list
for (const auto& outProfile : hwModule->getOutputProfiles()) {
//--------------------------------------------
}
// open input streams needed to access attached devices to validate
// mAvailableInputDevices list
for (const auto& inProfile : hwModule->getInputProfiles()) {
if (!inProfile->canOpenNewIo()) {
ALOGE("Invalid Input profile max open count %u for profile %s",
inProfile->maxOpenCount, inProfile->getTagName().c_str());
continue;
}
if (!inProfile->hasSupportedDevices()) {
ALOGW("Input profile contains no device on module %s", hwModule->getName());
continue;
}
// chose first device present in profile's SupportedDevices also part of
// available input devices
const DeviceVector &supportedDevices = inProfile->getSupportedDevices();
ALOGI("Supported devices for profile %s: %s", inProfile->getTagName().c_str(), supportedDevices.toString().c_str());
DeviceVector availProfileDevices = supportedDevices.filter(mAvailableInputDevices);
// 打印筛选后的可用设备列表
ALOGI("Available input devices after filter: %s", availProfileDevices.toString().c_str());
if (availProfileDevices.isEmpty()) {
ALOGE("%s: Input device list is empty!", __FUNCTION__);
continue;
}
// --------------------------------------------------
}
}
//----------------------------------------------------------
}
日志打印
// 在 Android 音频系统中,primary 是主要的音频硬件模块,负责处理 主声卡(main sound card) 的音频输入/输出。
APM_AudioPolicyManager pid-2137 I Processing hardware module: primary
APM_AudioPolicyManager pid-2137 I Available input devices for module primary: {type:0x80000004,@:;type:0x80000100,@:0}
APM_AudioPolicyManager pid-2137 I Available output devices for module primary: {type:0x2,@:}
APM_AudioPolicyManager pid-2137 I Supported devices for profile primary input: {type:0x80000004,@:;type:0x80000010,@:;type:0x80000008,@:}
APM_AudioPolicyManager pid-2137 I Available input devices after filter: {type:0x80000004,@:}
APM_AudioPolicyManager pid-2137 W Input profile contains no device on module primary
// A2DP(Advanced Audio Distribution Profile)是 蓝牙音频传输协议,用于 蓝牙音箱、耳机、车载音响等音频输出设备。
APM_AudioPolicyManager pid-2137 I Processing hardware module: a2dp
APM_AudioPolicyManager pid-2137 I Available input devices for module a2dp: {type:0x80000004,@:;type:0x80000100,@:0}
APM_AudioPolicyManager pid-2137 I Available output devices for module a2dp: {type:0x2,@:}
APM_AudioPolicyManager pid-2137 I Supported devices for profile a2dp input: {type:0x80020000,@:}
APM_AudioPolicyManager pid-2137 I Available input devices after filter: AUDIO_DEVICE_NONE
APM_AudioPolicyManager pid-2137 E initialize: Input device list is empty!
APM_AudioPolicyManager pid-2137 I Processing hardware module: usb
APM_AudioPolicyManager pid-2137 I Available input devices for module usb: {type:0x80000004,@:;type:0x80000100,@:0}
APM_AudioPolicyManager pid-2137 I Available output devices for module usb: {type:0x2,@:}
APM_AudioPolicyManager pid-2137 I Supported devices for profile usb_device input: {type:0x80001000,@:;type:0x82000000,@:}
APM_AudioPolicyManager pid-2137 I Available input devices after filter: AUDIO_DEVICE_NONE
APM_AudioPolicyManager pid-2137 E initialize: Input device list is empty!
// r_submix(Remote Submix)是 Android 的 虚拟音频设备
APM_AudioPolicyManager pid-2137 I Processing hardware module: r_submix
APM_AudioPolicyManager pid-2137 I Available input devices for module r_submix: {type:0x80000004,@:;type:0x80000100,@:0}
APM_AudioPolicyManager pid-2137 I Available output devices for module r_submix: {type:0x2,@:}
APM_AudioPolicyManager pid-2137 I Supported devices for profile r_submix input: {type:0x80000100,@:0}
APM_AudioPolicyManager pid-2137 I Available input devices after filter: {type:0x80000100,@:0}
设备类型定义
./system/media/audio/include/system/audio-base.h
enum {
AUDIO_DEVICE_IN_BUILTIN_MIC = 0x80000004u, // BIT_IN | 0x4
AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000008u, // BIT_IN | 0x8
AUDIO_DEVICE_IN_WIRED_HEADSET = 0x80000010u, // BIT_IN | 0x10
AUDIO_DEVICE_IN_REMOTE_SUBMIX = 0x80000100u, // BIT_IN | 0x100
AUDIO_DEVICE_IN_USB_DEVICE = 0x80001000u, // BIT_IN | 0x1000
}
分析
未识别到 AUDIO_DEVICE_IN_USB_DEVICE(0x80001000u) 设备
排查audio_policy
vendor/etc/audio_policy_configuration.xml
<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
<globalConfiguration speaker_drc_enabled="true"/>
<modules>
<module name="primary" halVersion="2.0">
<attachedDevices>
<item>Speaker</item>
<item>Built-In Mic</item>
</attachedDevices>
<defaultOutputDevice>Speaker</defaultOutputDevice>
<mixPorts>
<mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<!--
<mixPort name="hdmi output" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="44100,48000" channelMasks="dynamic"/>
</mixPort>
-->
<mixPort name="primary input" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO"/>
</mixPort>
<mixPort name="fast input" role="sink" flags="AUDIO_INPUT_FLAG_FAST">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO"/>
</mixPort>
</mixPorts>
<devicePorts>
<devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink">
</devicePort>
<devicePort tagName="Wired Headset" type="AUDIO_DEVICE_OUT_WIRED_HEADSET" role="sink">
</devicePort>
<devicePort tagName="Wired Headphones" type="AUDIO_DEVICE_OUT_WIRED_HEADPHONE" role="sink">
</devicePort>
<devicePort tagName="BT SCO" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO" role="sink">
</devicePort>
<devicePort tagName="BT SCO Headset" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET" role="sink">
</devicePort>
<devicePort tagName="BT SCO Car Kit" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT" role="sink">
</devicePort>
<devicePort tagName="HDMI Out" type="AUDIO_DEVICE_OUT_AUX_DIGITAL" role="sink">
</devicePort>
<devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
</devicePort>
<devicePort tagName="Wired Headset Mic" type="AUDIO_DEVICE_IN_WIRED_HEADSET" role="source">
</devicePort>
<devicePort tagName="BT SCO Headset Mic" type="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET" role="source">
</devicePort>
</devicePorts>
<routes>
<route type="mix" sink="Speaker"
sources="primary output"/>
<route type="mix" sink="Wired Headset"
sources="primary output"/>
<route type="mix" sink="Wired Headphones"
sources="primary output"/>
<route type="mix" sink="BT SCO"
sources="primary output"/>
<route type="mix" sink="BT SCO Headset"
sources="primary output"/>
<route type="mix" sink="BT SCO Car Kit"
sources="primary output"/>
<!--
<route type="mix" sink="HDMI Out"
sources="hdmi output"/>
-->
<route type="mix" sink="primary input"
sources="Built-In Mic,Wired Headset Mic,BT SCO Headset Mic"/>
</routes>
</module>
<!-- A2dp Audio HAL -->
<xi:include href="a2dp_audio_policy_configuration.xml"/>
<!-- Usb Audio HAL -->
<xi:include href="usb_audio_policy_configuration.xml"/>
<!-- Remote Submix Audio HAL -->
<xi:include href="r_submix_audio_policy_configuration.xml"/>
</modules>
<!-- Volume section -->
<xi:include href="audio_policy_volumes_drc.xml"/>
<xi:include href="ceres_volume_tables.xml"/>
</audioPolicyConfiguration>
vendor/etc/usb_audio_policy_configuration.xml
<!-- USB Audio HAL Audio Policy Configuration file -->
<module name="usb" halVersion="2.0">
<mixPorts>
<mixPort name="usb_accessory output" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="44100" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="usb_device output" role="source"/>
<mixPort name="usb_device input" role="sink"/>
</mixPorts>
<devicePorts>
<devicePort tagName="USB Host Out" type="AUDIO_DEVICE_OUT_USB_ACCESSORY" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="44100" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</devicePort>
<devicePort tagName="USB Device Out" type="AUDIO_DEVICE_OUT_USB_DEVICE" role="sink"/>
<devicePort tagName="USB Headset Out" type="AUDIO_DEVICE_OUT_USB_HEADSET" role="sink"/>
<devicePort tagName="USB Device In" type="AUDIO_DEVICE_IN_USB_DEVICE" role="source"/>
<devicePort tagName="USB Headset In" type="AUDIO_DEVICE_IN_USB_HEADSET" role="source"/>
</devicePorts>
<routes>
<route type="mix" sink="USB Host Out"
sources="usb_accessory output"/>
<route type="mix" sink="USB Device Out"
sources="usb_device output"/>
<route type="mix" sink="USB Headset Out"
sources="usb_device output"/>
<route type="mix" sink="usb_device input"
sources="USB Device In,USB Headset In"/>
</routes>
</module>
USB 相机插入内核日志
确认 USB 音频设备在内核中被识别
// A133 日志
[ 97.851385] usb 1-1.2: USB disconnect, device number 3
[ 108.244471] usb 1-1.2: new high-speed USB device number 4 using sunxi-ehci
[ 108.720295] uvcvideo: Found UVC 1.00 device WN Full HD CAMERA (1bcf:2281)
[ 108.750790] uvcvideo 1-1.2:1.0: Entity type for entity Extension 3 was not initialized!
[ 108.759967] uvcvideo 1-1.2:1.0: Entity type for entity Processing 2 was not initialized!
[ 108.769143] uvcvideo 1-1.2:1.0: Entity type for entity Camera 1 was not initialized!
[ 108.781324] input: WN Full HD CAMERA as /devices/platform/soc/5200000.ehci1-controller/usb1/1-1/1-1.2/1-1.2:1.0/input/input5
[ 108.845166] usb 1-1.2: Warning! Unlikely big volume range (=4096), cval->res is probably wrong.
[ 108.855330] usb 1-1.2: [5] FU [Mic Capture Volume] ch = 1, val = 0/4096/1
// rk3568 日志
[ 34.176719] usb 5-1: new high-speed USB device number 3 using xhci-hcd
[ 34.565305] usb 5-1: New USB device found, idVendor=1bcf, idProduct=2281, bcdDevice= 4.19
[ 34.565339] usb 5-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 34.565346] usb 5-1: Product: WN Full HD CAMERA
[ 34.565353] usb 5-1: Manufacturer: WN-ZW-220419
[ 34.822802] uvcvideo: Found UVC 1.00 device WN Full HD CAMERA (1bcf:2281)
[ 34.867343] input: WN Full HD CAMERA: WN Full HD C as /devices/platform/usbhost/fd000000.dwc3/xhci-hcd.1.auto/usb5/5-1/5-1:1.0/input/input6
[ 34.963920] usb 5-1: Warning! Unlikely big volume range (=4096), cval->res is probably wrong.
[ 34.963955] usb 5-1: [5] FU [Mic Capture Volume] ch = 1, val = 0/4096/1
lshal 对比
lshal(List HALs)是 Android 的一个命令行工具,用于列出 所有已注册的 HAL(Hardware Abstraction Layer)接口。它主要用于 调试和验证 HAL 层是否正确加载,特别是 音频、摄像头、USB 等设备的 HAL。
lshal 适用于 Treble 架构(Android 8.0+),可以用于查看 HIDL(HAL Interface Definition Language) 和 AIDL(Android Interface Definition Language) 两种 HAL。
A133:/ $ lshal | grep audio
DM,FC Y android.hardware.audio.effect@5.0::IEffectsFactory/default 0/3 1822 1839 1651
DM,FC Y android.hardware.audio@5.0::IDevicesFactory/default 0/3 1822 1839 1651
FC ? android.hardware.audio.effect@5.0::IEffectsFactory/default N/A 1822 1822
FC ? android.hardware.audio@5.0::IDevicesFactory/default N/A 1822 1822
FC ? android.hardware.audio.effect@5.0::I*/* (/vendor/lib/hw/) N/A N/A
FC ? android.hardware.audio@5.0::I*/* (/vendor/lib/hw/) N/A N/A
rk3568_r:/ $ lshal | grep audio
DM,FC Y android.hardware.audio.effect@6.0::IEffectsFactory/default 0/3 278 298 154
DM,FC Y android.hardware.audio@6.0::IDevicesFactory/default 0/3 278 298 154
FC ? android.hardware.audio.effect@6.0::IEffectsFactory/default N/A 278 278
FC ? android.hardware.audio@6.0::IDevicesFactory/default N/A 278 278
X ? android.hardware.audio.effect@6.0::I*/* (/vendor/lib/hw/) N/A N/A
X ? android.hardware.audio.effect@6.0::I*/* (/vendor/lib64/hw/) N/A N/A
X ? android.hardware.audio@6.0::I*/* (/vendor/lib/hw/) N/A N/A
X ? android.hardware.audio@6.0::I*/* (/vendor/lib64/hw/) N/A N/A
HwModule
frameworks/av/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
HwModule::HwModule(const char *name, uint32_t halVersionMajor, uint32_t halVersionMinor)
: mName(String8(name)),
mHandle(AUDIO_MODULE_HANDLE_NONE)
{
setHalVersion(halVersionMajor, halVersionMinor);
}
HwModule::~HwModule()
{
for (size_t i = 0; i < mOutputProfiles.size(); i++) {
mOutputProfiles[i]->clearSupportedDevices();
}
for (size_t i = 0; i < mInputProfiles.size(); i++) {
mInputProfiles[i]->clearSupportedDevices();
}
}
void HwModuleCollection::dump(String8 *dst) const
{
dst->append("\nHW Modules dump:\n");
for (size_t i = 0; i < size(); i++) {
dst->appendFormat("- HW Module %zu:\n", i + 1);
itemAt(i)->dump(dst);
}
}
sp<DeviceDescriptor> HwModuleCollection::createDevice(const audio_devices_t type,
const char *address,
const char *name,
const audio_format_t encodedFormat) const
{
sp<HwModule> hwModule = getModuleForDeviceTypes(type, encodedFormat);
if (hwModule == 0) {
ALOGE("%s: could not find HW module for device %04x address %s", __FUNCTION__, type,
address);
return nullptr;
}
sp<DeviceDescriptor> device = new DeviceDescriptor(type, String8(name));
device->setName(String8(name));
device->setAddress(String8(address));
device->setEncodedFormat(encodedFormat);
// Add the device to the list of dynamic devices
hwModule->addDynamicDevice(device);
// Reciprocally attach the device to the module
device->attach(hwModule);
ALOGD("%s: adding dynamic device %s to module %s", __FUNCTION__,
device->toString().c_str(), hwModule->getName());
const auto &profiles = (audio_is_output_device(type) ? hwModule->getOutputProfiles() :
hwModule->getInputProfiles());
for (const auto &profile : profiles) {
// Add the device as supported to all profile supporting "weakly" or not the device
// according to its type
if (profile->supportsDevice(device, false /*matchAdress*/)) {
// @todo quid of audio profile? import the profile from device of the same type?
const auto &isoTypeDeviceForProfile =
profile->getSupportedDevices().getDevice(type, String8(), AUDIO_FORMAT_DEFAULT);
device->importAudioPort(isoTypeDeviceForProfile, true /* force */);
ALOGV("%s: adding device %s to profile %s", __FUNCTION__,
device->toString().c_str(), profile->getTagName().c_str());
profile->addSupportedDevice(device);
}
}
return device;
}
rk3568插入USB摄像头正常日志
2025-02-21 14:11:40.070 305-421 APM::HwModule audioserver D createDevice: adding dynamic device type:0x80001000,@:card=2;device=0; to module usb
2025-02-21 14:11:40.071 305-421 APM::HwModule audioserver V createDevice: adding device type:0x80001000,@:card=2;device=0; to profile usb_device input
UsbAlsaManager
UsbAlsaManager 的工作原理就是在用户插入 USB 音频设备时,识别该设备并为其配置 ALSA 音频接口。它负责管理设备生命周期、路由音频流、音量控制和设备状态更新。通过与 ALSA 系统和 Android 音频策略系统的协作,UsbAlsaManager 确保 USB 音频设备能够与 Android 系统无缝集成。
A133 日志
2025-02-21 14:55:38.083 2382-3170 UsbAlsaManager system_server D usbDeviceAdded(): WN-ZW-220419 nm:WN Full HD CAMERA
2025-02-21 14:55:38.088 2382-3170 UsbAlsaManager system_server D hasInput: false hasOutput:false
2025-02-21 16:06:01.733 2072-2797 UsbDescriptorParser system_server D parseDescriptors() - end 76 descriptors.
2025-02-21 16:06:02.295 2072-2797 UsbDescriptorParser system_server D ---- hasInput()
2025-02-21 16:06:02.301 2072-2797 UsbDescriptorParser system_server D hasInput() = false
2025-02-21 14:55:38.088 2382-3170 UsbAlsaManager system_server D hasMidi: false mHasMidiFeature:true
2025-02-21 14:55:38.088 2382-3170 UsbAlsaManager system_server I deviceAdded()----------------
2025-02-21 14:55:38.088 2382-3170 UsbAlsaManager system_server I ----------------
2025-02-21 14:55:38.088 2382-3170 UsbAlsaManager system_server D deviceAdded() - done
rk3568日志
2025-02-21 15:37:53.775 2068-2797 UsbHostManager system_server D USB device attached: vidpid 1bcf:2281 mfg/product/ver/serial WN-ZW-220419/WN Full HD CAMERA/4.19/null hasAudio/HID/Storage: true/false/false
2025-02-21 15:37:53.790 2068-2797 UsbDeviceDescriptor system_server D 1 configs
2025-02-21 15:37:53.808 2068-2797 UsbHostManager system_server D Added device UsbDevice[mName=/dev/bus/usb/001/005,mVendorId=7119,mProductId=8833,mClass=239,mSubclass=2,mProtocol=1,mManufacturerName=WN-ZW-220419,mProductName=WN Full HD CAMERA,mVersion=4.19,mSerialNumberReader=com.android.server.usb.UsbSerialReader@47af08f,mConfigurations=[
UsbConfiguration[mId=1,mName=null,mAttributes=128,mMaxPower=250,mInterfaces=[
UsbInterface[mId=0,mAlternateSetting=0,mName=WN Full HD CAMERA,mClass=14,mSubclass=1,mProtocol=0,mEndpoints=[
2025-02-21 15:17:36.192 465-726 UsbAlsaManager system_process D usbDeviceAdded(): WN-ZW-220419 nm:WN Full HD CAMERA
2025-02-21 15:17:36.196 465-726 UsbAlsaManager system_process D hasInput: true hasOutput:false
2025-02-21 16:41:39.962 466-720 UsbDescriptorParser system_process D ---- hasInput()
2025-02-21 16:41:39.963 466-720 UsbDescriptorParser system_process D type:0x201
2025-02-21 16:41:39.963 466-720 UsbDescriptorParser system_process D hasInput() = true
2025-02-21 15:17:36.198 465-726 UsbAlsaManager system_process D selectAlsaDevice() UsbAlsaDevice: [card: 2, device: 0, name: USB-Audio - WN Full HD CAMERA, hasOutput: false, hasInput: true]
2025-02-21 15:17:36.200 465-726 UsbAlsaDevice system_process I INPUT JACK connected: true
2025-02-21 15:17:36.203 465-726 UsbAlsaManager system_process D selectAlsaDevice() - done.
2025-02-21 15:17:36.203 465-726 UsbAlsaManager system_process D hasMidi: false mHasMidiFeature:false
2025-02-21 15:17:36.203 465-726 UsbAlsaManager system_process I deviceAdded()----------------
2025-02-21 15:17:36.203 465-726 UsbAlsaManager system_process I [card:2 device:0 USB-Audio - WN Full HD CAMERA]
2025-02-21 15:17:36.203 465-726 UsbAlsaManager system_process I ----------------
2025-02-21 15:17:36.203 465-726 UsbAlsaManager system_process D deviceAdded() - done
源码
frameworks/base/services/usb/java/com/android/server/usb/UsbAlsaManager.java
public final class UsbAlsaManager {
/* package */ void usbDeviceAdded(String deviceAddress, UsbDevice usbDevice,
UsbDescriptorParser parser) {
if (DEBUG) {
Slog.d(TAG, "usbDeviceAdded(): " + usbDevice.getManufacturerName()
+ " nm:" + usbDevice.getProductName());
}
// Scan the Alsa File Space
mCardsParser.scan();
// Find the ALSA spec for this device address
AlsaCardsParser.AlsaCardRecord cardRec =
mCardsParser.findCardNumFor(deviceAddress);
if (cardRec == null) {
return;
}
// Add it to the devices list
boolean hasInput = parser.hasInput() // 关键点
&& !isDeviceBlacklisted(usbDevice.getVendorId(), usbDevice.getProductId(),
USB_BLACKLIST_INPUT);
boolean hasOutput = parser.hasOutput()
&& !isDeviceBlacklisted(usbDevice.getVendorId(), usbDevice.getProductId(),
USB_BLACKLIST_OUTPUT);
if (DEBUG) {
Slog.d(TAG, "hasInput: " + hasInput + " hasOutput:" + hasOutput);
}
if (hasInput || hasOutput) {
boolean isInputHeadset = parser.isInputHeadset();
boolean isOutputHeadset = parser.isOutputHeadset();
if (mAudioService == null) {
Slog.e(TAG, "no AudioService");
return;
}
UsbAlsaDevice alsaDevice =
new UsbAlsaDevice(mAudioService, cardRec.getCardNum(), 0 /*device*/,
deviceAddress, hasOutput, hasInput,
isInputHeadset, isOutputHeadset);
if (alsaDevice != null) {
alsaDevice.setDeviceNameAndDescription(
cardRec.getCardName(), cardRec.getCardDescription());
mAlsaDevices.add(0, alsaDevice);
selectAlsaDevice(alsaDevice);
}
}
//-------------------------------------------------------
logDevices("deviceAdded()");
if (DEBUG) {
Slog.d(TAG, "deviceAdded() - done");
}
}
}
frameworks/base/services/core/jni/com_android_server_UsbHostManager.cpp
static int usb_device_added(const char *devAddress, void* clientData) {
struct usb_device *device = usb_device_open(devAddress);
if (!device) {
ALOGE("usb_device_open failed\n");
return 0;
}
const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
int classID = deviceDesc->bDeviceClass;
int subClassID = deviceDesc->bDeviceSubClass;
// get the raw descriptors
int numBytes = usb_device_get_descriptors_length(device);
if (numBytes > 0) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jobject thiz = (jobject)clientData;
jstring deviceAddress = env->NewStringUTF(devAddress);
jbyteArray descriptorsArray = env->NewByteArray(numBytes);
const jbyte* rawDescriptors = (const jbyte*)usb_device_get_raw_descriptors(device);
env->SetByteArrayRegion(descriptorsArray, 0, numBytes, rawDescriptors);
env->CallBooleanMethod(thiz, method_usbDeviceAdded,
deviceAddress, classID, subClassID, descriptorsArray);
env->DeleteLocalRef(descriptorsArray);
env->DeleteLocalRef(deviceAddress);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
} else {
// TODO return an error code here?
ALOGE("error reading descriptors\n");
}
usb_device_close(device);
return 0;
}
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);
}
frameworks/base/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
public final class UsbDescriptorParser {
/**
* @hide
*/
public void parseDescriptors(byte[] descriptors) {
if (DEBUG) {
Log.d(TAG, "parseDescriptors() - start");
}
ByteStream stream = new ByteStream(descriptors);
while (stream.available() > 0) {
UsbDescriptor descriptor = null;
try {
descriptor = allocDescriptor(stream);
} catch (Exception ex) {
Log.e(TAG, "Exception allocating USB descriptor.", ex);
}
if (descriptor != null) {
// Parse
try {
descriptor.parseRawDescriptors(stream);
// Clean up
descriptor.postParse(stream);
} catch (Exception ex) {
Log.e(TAG, "Exception parsing USB descriptors.", ex);
// Clean up
descriptor.setStatus(UsbDescriptor.STATUS_PARSE_EXCEPTION);
} finally {
// Add descriptor to list
if (DEBUG) {
Log.d(TAG, "Adding descriptor to list: " + descriptor.getType());
}
mDescriptors.add(descriptor);
}
}
}
if (DEBUG) {
Log.d(TAG, "parseDescriptors() - end " + mDescriptors.size() + " descriptors.");
}
}
private UsbDescriptor allocDescriptor(ByteStream stream)
throws UsbDescriptorsStreamFormatException {
stream.resetReadCount();
int length = stream.getUnsignedByte();
byte type = stream.getByte();
UsbDescriptor descriptor = null;
switch (type) {
//--------------------------------------------------------
/*
* Audio Class Specific
*/
case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE:
// 关键点
Log.e(TAG, "DESCRIPTORTYPE_AUDIO_INTERFACE");
int devClass = mDeviceDescriptor.getDevClass();
Log.e(TAG, "Device Class: 0x" + Integer.toHexString(devClass));
if (devClass == UsbDescriptor.CLASSID_AUDIO) {
Log.e(TAG, "DESCRIPTORTYPE_AUDIO_INTERFACE-----UsbACInterface.allocDescriptor");
descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
} else {
Log.e(TAG, "Skipping non-audio USB device.");
}
break;
case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT:
descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
break;
default:
break;
}
if (descriptor == null) {
// Unknown Descriptor
Log.i(TAG, "Unknown Descriptor len: " + length + " type:0x"
+ Integer.toHexString(type));
descriptor = new UsbUnknown(length, type);
}
return descriptor;
/*
* Attribute predicates
*/
/**
* @hide
*/
public boolean hasInput() {
if (DEBUG) {
Log.d(TAG, "---- hasInput()");
}
// 获取音频控制接口描述符
ArrayList<UsbDescriptor> acDescriptors =
getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
UsbACInterface.AUDIO_AUDIOCONTROL);
boolean hasInput = false;
// 遍历 acDescriptors(音频控制描述符列表),检查是否存在 有效的音频输入终端。
for (UsbDescriptor descriptor : acDescriptors) {
if (descriptor instanceof UsbACTerminal) {
UsbACTerminal inDescr = (UsbACTerminal) descriptor;
// Check for input and bi-directional terminal types
int type = inDescr.getTerminalType();
if (DEBUG) {
Log.d(TAG, " type:0x" + Integer.toHexString(type));
}
int terminalCategory = type & ~0xFF;
if (terminalCategory != UsbTerminalTypes.TERMINAL_USB_UNDEFINED
&& terminalCategory != UsbTerminalTypes.TERMINAL_OUT_UNDEFINED) {
// If not explicitly a USB connection or output, it could be an input.
hasInput = true;
break;
}
} else {
Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
if (DEBUG) {
Log.d(TAG, "hasInput() = " + hasInput);
}
return hasInput;
}
}
日志
UsbDescriptorParser system_server E DESCRIPTORTYPE_AUDIO_INTERFACE
UsbDescriptorParser system_server E Device Class: 0xef
UsbDescriptorParser system_server E Skipping non-audio USB device.
UsbDescriptorParser system_server I Unknown Descriptor len: 9 type:0x24
从日志上分析int devClass = mDeviceDescriptor.getDevClass() 获取到的值是0xef, 而UsbDescriptor.CLASSID_AUDIO的值为0x01, 导致USB描述符无法解析成音频设备
综上分析: 该USB音频描述符无法识别
修改方案
diff --git a/android/frameworks/base/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/android/frameworks/base/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 87b0b77b12..ded5d05116 100755
--- a/android/frameworks/base/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/android/frameworks/base/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -177,15 +177,17 @@ public final class UsbDescriptorParser {
* Audio Class Specific
*/
case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE:
- if (mDeviceDescriptor.getDevClass() == UsbDescriptor.CLASSID_AUDIO) {
+ // delete by lixiaogang for usb mic
+ // if (mDeviceDescriptor.getDevClass() == UsbDescriptor.CLASSID_AUDIO) {
descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
- }
+ // }
break;
case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT:
- if (mDeviceDescriptor.getDevClass() == UsbDescriptor.CLASSID_AUDIO) {
+ // delete by lixiaogang for usb mic
+ // if (mDeviceDescriptor.getDevClass() == UsbDescriptor.CLASSID_AUDIO) {
descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
- }
+ // }
break;
default:
diff --git a/android/packages/apps/Camera2/src/com/android/camera/VideoModule.java b/android/packages/apps/Camera2/src/com/android/camera/VideoModule.java
index 7ee70c1a62..5e575c4337 100644
--- a/android/packages/apps/Camera2/src/com/android/camera/VideoModule.java
+++ b/android/packages/apps/Camera2/src/com/android/camera/VideoModule.java
@@ -1154,7 +1154,7 @@ public class VideoModule extends CameraModule
}
mMediaRecorder.setCamera(camera);
- mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
+ mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.UNPROCESSED);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setProfile(mProfile);