A133 音频设备

android 10

Posted by LXG on February 21, 2025

音频-AOSP

Android Framework 音频子系统(02)音频系统框架

AOSP 音频架构

ape_fwk_audio

查看音频硬件命令


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_audio

在 A133 中,存在 7 个音频设备,分别是:

a133_audio_hardware

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 主要解决以下问题:

  1. 不同 SoC 芯片的音频控制逻辑不同(如 Allwinner、Rockchip、Qualcomm、Amlogic)
  2. 音频编解码器(Codec)种类繁多(不同厂商使用不同的 DAC/ADC 芯片)
  3. 开发板的音频架构复杂(可能有多个 I2S 接口、多个音频通路)
  4. 降低驱动代码重复,方便移植和维护

🔹 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);