Create the HAL interface

You must use HIDL to describe all build flags used for conditionally compiling the framework. Relevant build flags must be grouped and included in a single .hal file. Using HIDL for specifying configuration items includes the following benefits:

  • Versioned (to add new config items, vendors/OEMs must explicitly extend the HAL)
  • Well-documented
  • Access control using SELinux
  • Sanity check for configuration items through the Vendor Test Suite (range check, inter-dependency check among items, etc.)
  • Auto-generated APIs in both C++ and Java

Identify build flags used by the framework

Start by identifying the build configs used to conditionally compile the framework, then abandon obsolete configs to make the set smaller. For example, the following set of build flags are identified for surfaceflinger:

  • TARGET_USES_HWC2
  • TARGET_BOARD_PLATFORM
  • TARGET_DISABLE_TRIPLE_BUFFERING
  • TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS
  • NUM_FRAMEBUFFER_SURFACE_BUFFERS
  • TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK
  • VSYNC_EVENT_PHASE_OFFSET_NS
  • SF_VSYNC_EVENT_PHASE_OFFSET_NS
  • PRESENT_TIME_OFFSET_FROM_VSYNC_NS
  • MAX_VIRTUAL_DISPLAY_DIMENSION

Create a HAL interface

Build configs for a subsystem are accessed through a HAL interface, while interfaces for giving configuration values are grouped in the HAL package android.hardware.configstore (currently at version 1.0). For example, to create a HAL interface file for surfaceflinger, in hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal:

package android.hardware.configstore@1.0;

interface ISurfaceFlingerConfigs {
    // TO-BE-FILLED-BELOW
};

After creating the .hal file, run hardware/interfaces/update-makefiles.sh to add the new .hal file to the Android.bp and Android.mk files.

Add functions for build flags

For each build flag, add a new function to the interface. For example, in hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal:

interface ISurfaceFlingerConfigs {
    disableTripleBuffering() generates(OptionalBool ret);
    forceHwcForVirtualDisplays() generates(OptionalBool ret);
    enum NumBuffers: uint8_t {
        USE_DEFAULT = 0,
        TWO = 2,
        THREE = 3,
    };
    numFramebufferSurfaceBuffers() generates(NumBuffers ret);
    runWithoutSyncFramework() generates(OptionalBool ret);
    vsyncEventPhaseOffsetNs generates (OptionalUInt64 ret);
    presentTimeOffsetFromSyncNs generates (OptionalUInt64 ret);
    maxVirtualDisplayDimension() generates(OptionalInt32 ret);
};

When adding a function:

  • Be concise with names. Avoid converting makefile variable names into function names and keep in mind that TARGET_ and BOARD_ prefixes are no longer necessary.
  • Add comments. Help developers understand the purpose of the config item, how it changes framework behavior, valid values, and other relevant information.

Function return types can be Optional[Bool|String|Int32|UInt32|Int64|UInt64]. Types are defined in types.hal in the same directory and wrap primitive values with a field that indicates if the value is specified by the HAL; if not, the default value is used.

struct OptionalString {
    bool specified;
    string value;
};

When appropriate, define the enum that best represents the type of the configuration item and use that enum as the return type. In the example above, the NumBuffers enum is defined to limit the number of valid values. When defining such custom data types, add a field or a enum value (for example, USE_DEFAULT) for denoting if the value is/isn't specified by the HAL.

It's not mandatory for a single build flag to become a single function in HIDL. Module owners can alternatively aggregate closely related build flags into a struct and have a function that returns that struct (doing so can reduce number of function calls).

For example, an option for aggregating two build flags into a single struct in hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal is:

 interface ISurfaceFlingerConfigs {
    // other functions here
    struct SyncConfigs {
        OptionalInt64 vsyncEventPhaseoffsetNs;
        OptionalInt64 presentTimeoffsetFromSyncNs;
    };
    getSyncConfigs() generates (SyncConfigs ret);
    // other functions here
};

Alternatives to a single HAL function

As an alternative to using a single HAL function for all build flags, the HAL interface also provides simple functions such as getBoolean(string key) and getInteger(string key). The actual key=value pairs are stored in separate files and the HAL service provides values by reading/parsing those files.

While this approach is easy to define, it doesn't include the benefits provided by HIDL (enforced versioning, ease of documentation, access control) and is therefore not recommended.

Single and multiple interfaces

The design of the HAL interface for configuration items presents two choices:

  • A single interface that covers all configuration items
  • Multiple interfaces, each of which covers a set of related configuration items

A single interface is easier but can become unmaintainable as more configuration items are added to the single file. In addition, access control isn't fine-grained, so a process that's granted access to the interface can read all configuration items (access to a partial set of configuration items can't be granted). Alternatively, if access isn't granted, configuration items can't be read.

Because of these issues, Android uses multiple interfaces with a single HAL interface for a group of related configuration items. For example, ISurfaceflingerConfigs for surfaceflinger-related configuration items, and IBluetoothConfigs for Bluetooth-related configuration items.