Hot plug handling in Composer HAL

Display capabilities (such as display modes and supported HDR types) can change dynamically on devices which have externally connected displays (via HDMI or DisplayPort), such as Android TV set-top-boxes (STB) and over-the-top (OTT) devices. This change can happen as a result of an HDMI hot plug signal, such as when the user switches from one display to another or boots the device without a connected display. From Android 12 on, changes have been made in the framework to handle hot plugging and dynamic display capabilities.

This page covers how to handle display hot plugs and changes in display capabilities in the Composer HAL implementation. Additionally it discusses how to manage the associated framebuffer and prevent race conditions in these situations.

Updating display capabilities

This section describes how the Android framework handles changes in display capabilities initiated by Composer HAL.

Before Android can handle changes in display capabilities properly, the OEM must implement Composer HAL such that it uses an onHotplug(display, connection=CONNECTED) to notify the framework of any changes to display capabilities. Once that is implemented, Android handles changes to display capabilities as follows:

  1. On detecting a change in display capabilities, the framework receives an onHotplug(display, connection=CONNECTED) notification.
  2. On receiving the notification, the framework drops its display state and re-creates it with the new capabilities from the HAL by using the getActiveConfig, getDisplayConfigs, getDisplayAttribute, getColorModes, getHdrCapabilities and getDisplayCapabilities methods.
  3. Once the framework recreates a new display state, it sends the onDisplayChanged callback to the apps which are listening for such events.

The framework reallocates the framebuffers on subsequent onHotplug(display, connection=CONNECTED) events. See Managing framebuffer memory for more information on how to handle this.

Handling common connection scenarios

This section covers how to properly handle various connection scenarios in your implementations when the primary display is connected and disconnected.

Having been built for mobile devices, the Android framework doesn't have built-in support for a disconnected primary display. Instead the HAL must replace the primary display with a placeholder display in its interactions with the framework in the case when a primary display is physically disconnected.

The following scenarios can occur in set-top boxes and TV dongles which have externally connected displays that can be disconnected. In order to implement support for these scenarios, use the information in the table below:

Scenario Handling
No connected display at boot time
  • Send a onHotplug(display, connection=CONNECTED) signal from the Composer HAL to the framework.
  • Replace the physical display state inside the Composer HAL with a placeholder display state.

    Note: We recommend the placeholder display to have a single supported mode with resolution of 1080x1920 and refresh rate of 60hz, as this display mode is supported by most apps.

Primary display is physically connected
Primary display is physically disconnected
  • Send another onHotplug(display, connection=CONNECTED) event from the Composer HAL to the framework.
  • Replace the physical display state inside the Composer HAL with a placeholder display state. The placeholder display must have a single display mode, so that the framework sends the onDisplayChanged callback to apps (since the set of supported modes have changed). This single display mode must match the last active mode of the physical display before disconnection, so that apps don’t receive configuration change events.

Managing framebuffer memory

When an already connected display receives a subsequent onHotplug(display, connection=CONNECTED) notification event, the framework reallocates the framebuffers associated with this display. Device implementations must anticipate this behaviour and properly manage the framebuffer memory. Use the following guidelines in your implementation:

  • Before sending subsequent onHotplug(display, connection=CONNECTED) notification events, make sure to release handles to the framebuffers so that the system can properly deallocate them, before reallocating them. If the deallocation doesn’t succeed, the reallocation may fail due to lack of memory.

  • We recommend allocating a dedicated memory pool for the framebuffers that is separate from the rest of the graphic memory buffer.

This is important because between deallocation and reallocation of the framebuffers, a third party process can attempt to allocate the graphics memory. If the same graphics memory pool is used by the framebuffer and if the graphics memory is full, the third party process can occupy the graphics memory previously allocated by a framebuffer, thus leaving insufficient memory for the framebuffer reallocation (or possibly fragmenting the memory space).

Using sequential config IDs to prevent race conditions

Race conditions can arise if the Composer HAL updates the supported display configs concurrently with the framework calling setActiveConfig or setActiveConfigWithConstraints. The solution is to implement Composer HAL to use sequential IDs and prevent this problem.

This section describes how the race conditions might occur, followed by details on how to implement Composer HAL so that it uses sequential IDs to prevent such conditions.

Consider the following sequence of events, when new, sequential IDs are NOT assigned to the new display configs, causing a race condition:

  1. The supported display config IDs are:

    • id=1, 1080x1920 60hz
    • id=2, 1080x1920 50hz
  2. The framework calls setActiveConfig(display, config=1).

  3. Concurrently, the Composer HAL processes a change of display configs and updates its internal state to a new set of display configs, shown as follows:

    • id=1, 2160x3840 60hz
    • id=2, 2160x3840 50hz
    • id=3, 1080x1920 60hz
    • id=4, 1080x1920 50hz
  4. Composer HAL sends an onHotplug event to the framework, to notify that the set of supported modes has changed.

  5. The Composer HAL receives setActiveConfig(display, config=1) (from step 2).

  6. The HAL interprets that the framework has requested a config change to 2160x3840 60hz, although in reality 1080x1920 60hz was desired.

The process using non-sequential ID assignments ends here with a misinterpretation of the desired config change.

Configuring Composer HAL to use sequential IDs

To avoid such race conditions, the OEM must implement the Composer HAL as follows:

  • When the Composer HAL updates the supported display configs, it assigns new, sequential IDs to the new display configs.
  • When the framework calls setActiveConfig or setActiveConfigWithConstraints with an invalid config ID, the Composer HAL ignores the call.

These steps serve to prevent race conditions as shown in the following discussion.

Consider the following sequence of events, when new, sequential IDs are assigned to the new display configs:

  1. The supported display config IDs are:

    • id=1, 1080x1920 60hz
    • id=2, 1080x1920 50hz
  2. The framework calls setActiveConfig(display, config=1).

  3. When a change of display configs is processed, the next set of config IDs are assigned starting from the next unused integer, shown as follows:

    • id=3, 2160x3840 60hz

    • id=4, 2160x3840 50hz

    • id=5, 1080x1920 60hz

    • id=6, 1080x1920 50hz

  4. The Composer HAL sends an onHotplug event to the framework, to notify that the set of supported modes has changed.

  5. The Composer HAL receives setActiveConfig(display, config=1) (from step 2).

  6. The Composer HAL ignores the call as the ID is no longer valid.

  7. The framework receives and processes the onHotplug event from step 4. It calls into the Composer HAL using the functions getDisplayConfigs and getDisplayAttribute. With these functions the framework identifies the new ID (5) for the desired resolution and refresh rate of 1080x1920 and 60Hz.

  8. The framework sends another setActiveConfig event with an updated ID of 5.

  9. The Composer HAL receives setActiveConfig(display, config=5) from step 5.

  10. The HAL correctly interprets that the framework has requested a config change to 1080x1920 60hz.

As shown in the example above, the process using sequential ID assignments ensures that the race condition is prevented and the correct display config change is updated.