Android 9 deprecates the AUDIO_*
properties in previous iterations of the Vehicle HAL and replaces them with a
dedicated Audio Control HAL that includes explicit function calls and typed
parameter lists.
This new HAL exposes IAudioControl
as the primary interface
object that provides entry points to interact with the vehicle's audio engine
for configuration and volume control. The system can contain exactly one
instance of this object, which is created by CarAudioService
when
it starts up. This object is an automotive extension of the traditional
Android Audio HAL; in most implementations, the same process that publishes
the Audio HAL interfaces should also publish the
IAudioControl interfaces
.
Supported interfaces
The AudioControl
HAL supports the following interfaces:
getBusforContext
. Called at startup once per context to get the mapping fromContextNumber
tobusAddress
. Example usage:getBusForContext(ContextNumber contextNumber) generates (uint32_t busNumber);
Enables the vehicle to tell the framework where to route the physical output stream for each context. For every context, a valid bus number (0 - num busses-1) must be returned. If an unrecognizedcontextNumber
is encountered, -1 shall be returned. Any context for which an invalidbusNumber
is returned will be routed to bus 0.
Any concurrent sounds associated with the samebusNumber
via this mechanism will be mixed by the AndroidAudioFlinger
before being delivered as a single stream to the Audio HAL. This supersedes the Vehicle HAL propertiesAUDIO_HW_VARIANT
andAUDIO_ROUTING_POLICY
.setBalanceTowardRight
. Control the right/left balance setting of vehicle speakers. Example usage:setBalanceTowardRight(float value);
Shifts the speaker volume toward the right (+) or left (-) side of the car. 0.0 is centered, +1.0 is fully right, -1.0 is fully left, and a value outside the range -1 to 1 is an error.setFadeTowardFront
. Control the fore/aft fade setting of vehicle speakers. Example usage:setFadeTowardFront(float value);
Shifts the speaker volume toward the front (+) or back (-) of the car. 0.0 is centered, +1.0 is fully forward, -1.0 is fully rearward, and a value outside the range -1 to 1 is an error.
Configuring volume
Android automotive implementations should control volume using a hardware
amplifier instead of a software mixer. To avoid side effects, in
device/generic/car/emulator/audio/overlay/frameworks/base/core/res/res/values/config.xml
,
set the config_useFixedVolume
flag to true
(overlay
as necessary):
<resources> <!-- Car uses hardware amplifier for volume. --> <bool name="config_useFixedVolume">true</bool> </resources>
When the config_useFixedVolume
flag is not set (or set to
false
), applications can call
AudioManager.setStreamVolume()
and change the volume by stream
type in the software mixer. This may be undesirable because of the potential
effect on other applications and the fact that volume attenuation in the
software mixer results in fewer significant bits available in the signal when
received at the hardware amplifier.
Configuring volume groups
CarAudioService
uses volume groups defined in
packages/services/Car/service/res/xml/car_volume_group.xml
. You
can override this file to redefine volume groups as necessary. Groups are
identified at runtime by their order of definition in the XML file. IDs range
from 0 to N-1, where N is the number of volume groups. Example:
<volumeGroups xmlns:car="http://schemas.android.com/apk/res-auto"> <group> <context car:context="music"/> <context car:context="call_ring"/> <context car:context="notification"/> <context car:context="system_sound"/> </group> <group> <context car:context="navigation"/> <context car:context="voice_command"/> </group> <group> <context car:context="call"/> </group> <group> <context car:context="alarm"/> </group> </volumeGroups>
The attributes used in this configuration are defined in
packages/services/Car/service/res/values/attrs.xml
.
Handling volume key events
Android defines several keycodes for volume control, including
KEYCODE_VOLUME_UP
, KEYCODE_VOLUME_DOWN
, and
KEYCODE_VOLUME_MUTE
. By default, Android routes the volume key
events to applications. Automotive implementations should force these key
events to CarAudioService
, which can then call
setGroupVolume
or setMasterMute
as appropriate.
To force this behavior, in
device/generic/car/emulator/car/overlay/frameworks/base/core/res/res/values/config.xml
,
set the config_handleVolumeKeysInWindowManager
flag to
true
:
<resources> <bool name="config_handleVolumeKeysInWindowManager">true</bool> </resources>
CarAudioManager API
The CarAudioManager
uses CarAudioService
to
configure and control vehicle audio systems. The manager is invisible to most
apps in the system, but vehicle-specific components, such as a volume
controller, can use the CarAudioManager
API to interact with the
system.
The following sections describe Android 9 changes to
the CarAudioManager API
.
Deprecated APIs
Android 9 handles device enumeration through the
existing AudioManager
getDeviceList
API, so the
following vehicle-specific functions have been deprecated and removed:
String[] getSupportedExternalSourceTypes()
String[] getSupportedRadioTypes()
Android 9 handles volume using
AudioAttributes.AttributeUsage
or volume group-based entry
points, so the following APIs that rely on streamType
have been
removed:
void setStreamVolume(int streamType, int index, int flags)
int getStreamMaxVolume(int streamType)
int getStreamMinVolume(int streamType)
void setVolumeController(IVolumeController controller)
New APIs
Android 9 adds the following new APIs for controlling amplifier hardware (explicitly based on volume groups):
int getVolumeGroupIdForUsage(@AudioAttributes.AttributeUsage int usage)
int getVolumeGroupCount()
int getGroupVolume(int groupId)
int getGroupMaxVolume(int groupId)
int getGroupMinVolume(int groupId)
In addition, Android 9 provides the following new system APIs for use by System GUI:
void setGroupVolume(int groupId, int index, int flags)
void registerVolumeChangeObserver(@NonNull ContentObserver observer)
void unregisterVolumeChangeObserver(@NonNull ContentObserver observer)
void registerVolumeCallback(@NonNull IBinder binder)
void unregisterVolumeCallback(@NonNull IBinder binder)
void setFadeToFront(float value)
Void setBalanceToRight(float value)
Finally, Android 9 adds new APIs for external source management. These are intended primarily to support audio routing from external sources to the output buses based on media type. They can also potentially enable third-party application access to external devices.
String[] getExternalSources()
. Returns an array of addresses identifying the available audio ports in the system of typeAUX_LINE
,FM_TUNER
,TV_TUNER
, andBUS_INPUT
.CarPatchHandle createAudioPatch(String sourceAddress, int carUsage)
. Routes the source addresses to the outputBUS
associated with the providedcarUsage
.int releaseAudioPatch(CarPatchHandle patch)
. Removes the provided patch. If the creator of theCarPatchHandle
dies unexpectedly, this is handled automatically byAudioPolicyService::removeNotificationClient()
.
Creating audio patches
You can create an audio patch between two audio ports, either a mix port or a device port. Typically, an audio patch from mix port to device port is for playback while the reverse direction is for capture.
For example, an audio patch that routes audio samples from FM_TUNER
source directly to media sink bypasses the software mixer. You must then use a
hardware mixer to mix the audio samples from Android and FM_TUNER
for the sink. When creating an audio patch directly from FM_TUNER
source to the media sink:
- Volume control applies to the media sink and should affect both the
Android and
FM_TUNER
audio. - Users should be able to switch between Android and
FM_TUNER
audio via a simple app switch (no explicit media source choice should be necessary).
Automotive implementations might also need to create an audio patch between
two device ports. To do so, you must first declare the device ports and
possible routes in audio_policy_configuration.xml
and associate
mixports with these device ports.
Example configuration
See also
device/generic/car/emulator/audio/audio_policy_configuration.xml
.
<audioPolicyConfiguration> <modules> <module name="primary" halVersion="3.0"> <attachedDevices> <item>bus0_media_out</item> <item>bus1_audio_patch_test_in</item> </attachedDevices> <mixPorts> <mixPort name="mixport_bus0_media_out" 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="mixport_audio_patch_in" role="sink"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/> </mixPort> </mixPorts> <devicePorts> <devicePort tagName="bus0_media_out" role="sink" type="AUDIO_DEVICE_OUT_BUS" address="bus0_media_out"> <profile balance="" 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> <devicePort tagName="bus1_audio_patch_test_in" type="AUDIO_DEVICE_IN_BUS" role="source" address="bus1_audio_patch_test_in"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_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_media_out" sources="mixport_bus0_media_out,bus1_audio_patch_test_in"/> <route type="mix" sink="mixport_audio_patch_in" sources="bus1_audio_patch_test_in"/> </routes> </module> </modules> </audioPolicyConfiguration>
Audio driver API
You can use getExternalSources()
to retrieve a list of available
sources (identified by address), then create audio patches between these
sources and the sink ports by audio usages. The corresponding entry points on
the Audio HAL appear in IDevice.hal
:
Interface IDevice { ... / * Creates an audio patch between several source and sink ports. The handle * is allocated by the HAL and must be unique for this audio HAL module. * * @param sources patch sources. * @param sinks patch sinks. * @return retval operation completion status. * @return patch created patch handle. */ createAudioPatch(vec<AudioPortConfig> sources, vec<AudioPortConfig> sinks) generates (Result retval, AudioPatchHandle patch); * Release an audio patch. * * @param patch patch handle. * @return retval operation completion status. */ releaseAudioPatch(AudioPatchHandle patch) generates (Result retval); ... }
Configuring the volume settings UI
Android 9 decouples the volume settings UI from volume group configuration (which can be overlaid as described in Configuring volume groups). This separation ensures that no changes are required if the volume groups configuration changes in the future.
In car settings UI, the
packages/apps/Car/Settings/res/xml/car_volume_items.xml
file
contains UI elements (title and icon resources) associated with each defined
AudioAttributes.USAGE
. This file provides for a reasonable
rendering of the defined VolumeGroups by using resources associated with the
first recognized usage contained in each VolumeGroup.
For example, the following example defines a VolumeGroup as including both
voice_communication
and
voice_communication_signalling
. The default implementation of the
car settings UI renders the VolumeGroup using the resources associated with
voice_communication
as that is the first match in the file.
<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto"> <item car:usage="voice_communication" car:title="@*android:string/volume_call" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="voice_communication_signalling" car:title="@*android:string/volume_call" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="media" car:title="@*android:string/volume_music" car:icon="@*android:drawable/ic_audio_media"/> <item car:usage="game" car:title="@*android:string/volume_music" car:icon="@*android:drawable/ic_audio_media"/> <item car:usage="alarm" car:title="@*android:string/volume_alarm" car:icon="@*android:drawable/ic_audio_alarm"/> <item car:usage="assistance_navigation_guidance" car:title="@string/navi_volume_title" car:icon="@drawable/ic_audio_navi"/> <item car:usage="notification_ringtone" car:title="@*android:string/volume_ringtone" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="assistant" car:title="@*android:string/volume_unknown" car:icon="@*android:drawable/ic_audio_vol"/> <item car:usage="notification" car:title="@*android:string/volume_notification" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="notification_communication_request" car:title="@*android:string/volume_notification" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="notification_communication_instant" car:title="@*android:string/volume_notification" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="notification_communication_delayed" car:title="@*android:string/volume_notification" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="notification_event" car:title="@*android:string/volume_notification" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="assistance_accessibility" car:title="@*android:string/volume_notification" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="assistance_sonification" car:title="@*android:string/volume_unknown" car:icon="@*android:drawable/ic_audio_vol"/> <item car:usage="unknown" car:title="@*android:string/volume_unknown" car:icon="@*android:drawable/ic_audio_vol"/> </carVolumeItems>
The attributes and values used in the above configuration are declared in
packages/apps/Car/Settings/res/values/attrs.xml
. The volume
settings UI uses the following VolumeGroup
-based
CarAudioManager
APIs:
getVolumeGroupCount()
to know how many controls should be drawn.getGroupMinVolume()
andgetGroupMaxVolume()
to get lower and upper bounds.getGroupVolume()
to get the current volume.registerVolumeChangeObserver()
to get notified on volume changes.