Implementing the Audio HAL

Automotive audio implementations rely on the standard Android Audio HAL, which includes the following:

  • IDevice (hardware/interfaces/audio/2.0/IDevice.hal). Creates input and output streams, handles master volume and muting, and uses:
    • createAudioPatch to create external-external patches between devices.
    • IDevice.setAudioPortConfig() to provide volume for each physical stream.
  • IStream (hardware/interfaces/audio/2.0/IStream.hal). Along with its In and Out variants, manages the actual streaming of audio samples to and from the hardware.

Automotive device types

The following device types are relevant for automotive platforms:

Device type Description
AUDIO_DEVICE_OUT_BUS Primary output from Android (this is how all audio from Android is delivered to the vehicle). Used as the address for disambiguating streams for each Context.
AUDIO_DEVICE_OUT_TELEPHONY_TX Used for audio routed to the cellular radio for transmission.
AUDIO_DEVICE_IN_BUS Used for inputs not otherwise classified.
AUDIO_DEVICE_IN_FM_TUNER Used only for broadcast radio input.
AUDIO_DEVICE_IN_TV_TUNER May be used for a TV device if present.
AUDIO_DEVICE_IN_LINE Used for AUX input jack.
AUDIO_DEVICE_IN_BLUETOOTH_A2DP Music received over Bluetooth.
AUDIO_DEVICE_IN_TELEPHONY_RX Used for audio received from the cellular radio associated with a phone call.

Routing audio sources

Most audio sources should be captured using AudioRecord or a related Android mechanism. The data can then be assigned AudioAttributes and played through AndroidTrack either by relying on the default Android routing logic or by explicitly calling setPreferredDevice() on the AudioRecord and/or AudioTrack objects.

For sources with dedicated hardware connections to the external mixer or with extremely tight latency requirements, you can use createAudioPatch() and releaseAudioPatch() to activate and deactivate routes between external devices (without involving AudioFlinger in the transport of samples).

Configuring audio devices

Audio devices visible to Android must be defined in system/etc/audio_policy_configuration.xml, which includes the following components:

  • module name. Supports "primary" (used for automotive use cases), "A2DP", "remote_submix", and "USB". The module name and the corresponding audio driver should be compiled to audio.primary.$(variant).so.
  • devicePorts. Contains a list of device descriptors for all input and output devices (includes permanently attached devices and removable devices) that are accessible from this module.
    • For each output device, you can define gain control that consists of min/value/step/default values in millibel (1 millibel = 1/100 dB = 1/1000 bel).
    • The address attribute on a devicePort can be used to find the device, even if there are multiple devices with the same device type as AUDIO_DEVICE_OUT_BUS.
  • mixPorts. Contains a list of all output and input streams exposed by the audio HAL. Each mixPort can be considered as a physical stream to Android AudioService.
  • routes. Defines a list of possible connections between input and output devices or between stream and device.

The following example defines an output device bus0_phone_out in which all Android audio streams are mixed by mixer_bus0_phone_out. The route takes output stream of mixer_bus0_phone_out to device bus0_phone_out.

<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
    <modules>
        <module name="primary" halVersion="3.0">
            <attachedDevices>
                <item>bus0_phone_out</item>
<defaultOutputDevice>bus0_phone_out</defaultOutputDevice>
            <mixPorts>
                <mixPort name="mixport_bus0_phone_out" 
                         role="source"
                         flags="AUDIO_OUTPUT_FLAG_PRIMARY">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                            samplingRates="48000"
                            channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                </mixPort>
            </mixPorts>
            <devicePorts>
                <devicePort tagName="bus0_phone_out" 
                            role="sink"
                            type="AUDIO_DEVICE_OUT_BUS"
                            address="BUS00_PHONE">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                            samplingRates="48000"
                            channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                    <gains>
                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
                                minValueMB="-8400" 
                                maxValueMB="4000" 
                                defaultValueMB="0" 
                                stepValueMB="100"/>
                    </gains>
                </devicePort>
            </devicePorts>
            <routes>
                <route type="mix" sink="bus0_phone_out"
                       sources="mixport_bus0_phone_out"/>
            </routes>
        </module>
    </modules>
</audioPolicyConfiguration>

Specifying devicePorts

Automotive platforms should specify a devicePort for each physical stream that is input to and output from Android. For output, devicePorts should be of type AUDIO_DEVICE_OUT_BUS, and addressed by integers (i.e., Bus 0, Bus 1, etc.). mixPorts should be created in 1:1 relation to the devicePorts and should allow specification of the data formats that can be routed to each bus.

Automotive implementations can use multiple input device types, including FM_TUNER (reserved for broadcast radio input), MIC device for handling microphone input, and TYPE_AUX_LINE for representing analog line input. All other input streams are assigned to the AUDIO_DEVICE_IN_BUS and discovered by enumerating the devices via a AudioManager.getDeviceList() call. Individual sources can be differentiated by their AudioDeviceInfo.getProductName().

You can also define external devices as ports, then use those ports to interact with external hardware with the IDevice::createAudioPatch method of the Audio HAL (exposed via a new CarAudioManager entry point).

When the BUS-based audio driver is present, you must set the audioUseDynamicRouting flag to true:

<resources>
    <bool name="audioUseDynamicRouting">true</bool>
</resources>

For details, refer to device/generic/car/emulator/audio/overlay/packages/services/Car/service/res/values/config.xml.