Pulseaudio 4A (AGL Advanced Audio Agent) Client Module

This module allows pulseaudio to work as a client of the AGL Advanced Audio Agent.



This library is used to communicate with AFB (Application Framework Binder), the interface that is being used by 4A.


This is needed for the systemd event loop implementation which is a hard requirement of libafbwsc.


This is needed for json message parsing, also a hard requirement of libafbwsc.


This is meant to be built as part of the AGL distribution, using its bitbake recipe. It depends on an AGL-specific patch on pulseaudio that installs the module development headers on the system. On other distributions the source files of this repository should be copied into the pulseaudio source tree and pulseaudio’s build system should be adjusted accordingly.

On AGL, pulseaudio is enabled by the ‘agl-pulseaudio’ feature, which is also included by default in the ‘agl-demo’ feature. If you wish to disable it explicitly on the demo, you can use the ‘agl-demo-nopulseaudio’ feature instead.


  • module-4a-client initially calls ahl-4a/get_roles to retrieve the available roles in 4a
  • When a new sink-input appears, it uses its media.role property and the available roles to figure out which role it should use
  • if the role turns out to be empty, it uses the default_role parameter to assign a role to it
  • it calls ahl-4a/ {"action": "open"} to open the device and get an ALSA device uri
  • it loads module-alsa-sink with the device uri set to whatever ahl-4a returned
  • if all goes well, it moves the new sink-input to this new alsa sink
  • otherwise it takes no action and expects pulseaudio configuration to be set up in such a way so that the sink-input is muted
  • when the sink-input disappears, it unloads the relevant module-alsa-sink instance and calls ahl-4a/ {"action": "close"} to close the device in 4A


module-4a-client is recommended to be loaded from pulseaudio’s default.pa config file and it should be loaded in combination with a null sink:

load-module module-null-sink sink_name=aaaa_null_sink sink_properties="device.description='4A Null Output'"
set-default-sink aaaa_null_sink
load-module module-4a-client

This is because it expects that all new clients will automatically connect to a sink that is not one of the sinks that it loads internally.

Additionally, for correct operation, it should not be loaded together with one of the following:

  • module-alsa-card
  • module-udev-detect
  • module-detect
  • module-default-device-restore
  • module-role-cork

… or anything else that might automatically load new sinks, move clients on other sinks and alter stream volumes (volumes are meant to be handled by 4A, pulseaudio should be dummy in this usage scenario)


The module accepts two parameters, both optional.

  • ‘uri’ - The URI of the socket that will be used to talk to ahl-4a. Defaults to ‘unix:/run/user/0/apis/ws/ahl-4a’

  • ‘default_role’ - The name of the role that will be used as a fallback for clients that do not specify ‘media.role’. Defaults to ‘multimedia’


With PulseAudio started by systemd

On AGL, if PulseAudio is enabled on the image (enabled by default), it will be started automatically by systemd. PulseAudio uses the socket activation feature of systemd, so it will only be loaded on demand if a client tries to connect to it.

If you want to trigger manual activation, you can do so using systemctl: # systemctl --user start pulseaudio

Note: on the demo environment, some pulseaudio tools like ‘pacmd’ may not trigger socket activation because they are running as root, which is prohibited by pulseaudio. In order to use them, start pulseaudio manually first, or start a client.

Starting PulseAudio manually

Save this example configuration in a file called config.pa: ``` #!/usr/bin/pulseaudio -nF


load-module module-augment-properties load-module module-native-protocol-unix

load-module module-null-sink sink_name=aaaa_null_sink sink_properties=”device.description=’4A Null Output’” set-default-sink aaaa_null_sink load-module module-4a-client

load-module module-rescue-streams load-module module-always-sink load-module module-suspend-on-idle ```

Start pulseaudio with the above example config: # pulseaudio -nF config.pa ...

Running a PulseAudio client

Here is a simple gstreamer-based client you can run for testing on a terminal: # gst-launch-1.0 audiotestsrc ! pulsesink stream-properties="props,media.role=(string)emergency"

You can substitute “emergency” with any other available role, or just omit the whole stream-properties=… part to get assigned the default role (“multimedia”)

In your own application, in C, you can assign the role by setting this property on the pulsesink GStreamer element like this:

GstElement *pulsesink = ...;
GstStructure *s = gst_structure_new ("props",
    "media.role", G_TYPE_STRING, "emergency", NULL);
g_object_set (pulsesink, "stream-properties", s, NULL);
gst_structure_unref (s);

Missing functionality

The following functionality still needs to be implemented:

  • Handle source-outputs in the same way as sink-inputs
  • Cork sink-inputs that have been denied access to the device, so that the client can act on it.
  • Enforce security requirements - no other client should be allowed to connect to the internally loaded alsa sinks
  • Propagate the SMACK label of clients to 4A - this is supposed to allow 4A to restrict which clients are allowed to output audio