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:

Identifying 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:

Creating a HAL interface

Build configs for a subsystem are accessed via 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.

Adding 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:

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 (e.g., USE_DEFAULT) for denoting if the value is/is not specified by HAL.

It is 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 does not include the benefits provided by HIDL (enforced versioning, ease of documentation, access control) and is therefore not recommended.

Note: When using simple functions, access control is almost impossible as HAL cannot identify clients by itself.

Single vs. multiple interfaces

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

  1. Single interface that covers all configuration items
  2. 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 is not fine-grained, so a process granted access to the interface can read all configuration items (access to a partial set of configuration items cannot be granted). Alternatively, if access is not granted, no configuration item can 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, IBluetoothConfigs for Bluetooth-related configuration items, etc.