You can refactor conditionally-compiled code to read values dynamically from the HAL interface. For example:

#ifdef TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS
//some code fragment
#endif

Framework code can then call an appropriate utility function defined in <configstore/Utils.h> depending on its type.

ConfigStore example

This example shows reading TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS, defined in ConfigStore HAL as forceHwcForVirtualDisplays() with return type OptionalBool:

#include <configstore/Utils.h>
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;

static bool vsyncPhaseOffsetNs = getBool<ISurfaceFlingerConfigs,
        ISurfaceFlingerConfigs::forceHwcForVirtualDisplays>(false);

The utility function (getBool in the example above) contacts the configstore service to get the handle for the proxy of the interface function, then retrieves the value by invoking the handle via HIDL/hwbinder.

Utility functions

<configstore/Utils.h> (configstore/1.0/include/configstore/Utils.h) provides utility functions for each primitive return type, including Optional[Bool|String|Int32|UInt32|Int64|UInt64], as listed below:

Type Function (template parameters omitted)
OptionalBool bool getBool(const bool defValue)
OptionalInt32 int32_t getInt32(const int32_t defValue)
OptionalUInt32 uint32_t getUInt32(const uint32_t defValue)
OptionalInt64 int64_t getInt64(const int64_t defValue)
OptionalUInt64 uint64_t getUInt64(const uint64_t defValue)
OptionalString std::string getString(const std::string &defValue)

defValue is a default value returned when the HAL implementation does not specify a value for the configuration item. Each function takes two template parameters:

Because the configuration value is read-only and does not change, the utility function internally caches the configuration value. Subsequent calls are serviced more efficiently using the cached value in the same linking unit.

Using configstore-utils

The ConfigStore HAL is designed to be forward-compatible for minor version upgrades, meaning that when the HAL is up-revisioned and some framework code uses the newly-introduced items, the ConfigStore service with older minor version in /vendor can still be used.

For forward-compatibility, ensure your implementation adheres to the following guidelines:

  1. New items use the default value when only the old version service is available. Example:
    service = V1_1::IConfig::getService(); // null if V1_0 is installed
    value = DEFAULT_VALUE;
      if(service) {
        value = service->v1_1API(DEFAULT_VALUE);
      }
    
  2. Client uses the earliest interface in which the ConfigStore item was introduced. Example:
    V1_1::IConfig::getService()->v1_0API(); // NOT ALLOWED
    
    V1_0::IConfig::getService()->v1_0API(); // OK
    
  3. New version service can be retrieved for old version interface. In the following example, if the installed version is v1_1, the v1_1 service must be returned for getService():
    V1_0::IConfig::getService()->v1_0API();
    

    Note: The current AOSP implementation satisfies this requirement.

When the access functions in configstore-utils library are used for accessing the ConfigStore item, #1 is guaranteed by the implementation and #2 is guaranteed by compiler errors. For these reasons we strongly recommend using configstore-utils wherever possible.