Implementing Health 2.1

In Android 11, all healthd code is refactored into libhealthloop and libhealth2impl, then modified to implement the health@2.1 HAL. These two libraries are linked statically by health@2.0-impl-2.1, the passthrough implementation of health 2.1. The statically linked libraries enable health@2.0-impl-2.1 to do the same work as healthd, such as running healthd_mainloop and polling. In init, the health@2.1-service registers an implementation of the interface IHealth to hwservicemanager. When upgrading devices with an Android 8.x or 9 vendor image and an Android 11 framework, the vendor image might not provide the health@2.1 service. Backwards compatibility with old vendor images is enforced by the deprecation schedule.

To ensure backwards compatibility:

  1. healthd registers IHealth to hwservicemanager despite being a system daemon. IHealth is added to the system manifest, with the instance name "backup".
  2. The framework and storaged communicate with healthd via hwbinder instead of binder.
  3. The code for framework and storaged are changed to fetch the instance "default" if available, then "backup".
    • C++ client code uses the logic defined in libhealthhalutils.
    • Java client code uses the logic defined in HealthServiceWrapper.
  4. After IHealth/default is widely available and Android 8.1 vendor images are deprecated, IHealth/backup and healthd can be deprecated. For more details, see Deprecating health@1.0.

Board-specific build variables for healthd

BOARD_PERIODIC_CHORES_INTERVAL_* are board-specific variables used to build healthd. As part of the system/vendor build split, board-specific values cannot be defined for system modules. These values used to be overridden in the deprecated function healthd_board_init.

In health@2.1, vendors can override these two periodic chores interval values in the healthd_config struct before passing to the health implementation class constructor. The health implementation class should inherit from android::hardware::health::V2_1::implementation::Health.

Implementing the Health 2.1 service

For information on implementing the Health 2.1 service, see hardware/interfaces/health/2.1/README.md.

Health clients

health@2.x has the following clients:

  • charger. The use of libbatterymonitor and healthd_common code is wrapped in health@2.0-impl.
  • recovery. The linkage to libbatterymonitor is wrapped in health@2.0-impl. All calls to BatteryMonitor are replaced by calls into the Health implementation class.
  • BatteryManager. BatteryManager.queryProperty(int id) was the only client of IBatteryPropertiesRegistrar.getProperty. IBatteryPropertiesRegistrar.getProperty was provided by healthd and directly read /sys/class/power_supply.

    As a security consideration, apps aren't allowed to call into health HAL directly. In Android 9 and higher, the binder service IBatteryPropertiesRegistrar is provided by BatteryService instead of healthd. BatteryService delegates the call to the health HAL to retrieve the requested information.

  • BatteryService. In Android 9 and higher, BatteryService uses HealthServiceWrapper to determine whether to use the default health service instance from vendor or to use the backup health service instance from healthd. BatteryService then listens for health events via IHealth.registerCallback.

  • Storaged. In Android 9 and higher, storaged uses libhealthhalutils to determine whether to use the default health service instance from vendor or to use the backup health service instance from healthd. storaged then listens for health events via IHealth.registerCallback and retrieves storage information.

SELinux changes

The health@2.1 HAL includes the following SELinux changes in the platform:

  • Adds android.hardware.health@2.1-service to file_contexts.

For devices with their own implementation, some vendor SELinux changes may be necessary. Example:

# device/<manufacturer>/<device>/sepolicy/vendor/hal_health_default.te
# Add device specific permissions to hal_health_default domain, especially
# if it links to board-specific libhealthd or implements storage APIs.

Kernel interfaces

The healthd daemon and the default implementation android.hardware.health@2.0-impl-2.1 access the following kernel interfaces to retrieve battery information:

  • /sys/class/power_supply/*/capacity_level (added in health 2.1)
  • /sys/class/power_supply/*/capacity
  • /sys/class/power_supply/*/charge_counter
  • /sys/class/power_supply/*/charge_full
  • /sys/class/power_supply/*/charge_full_design (added in health 2.1)
  • /sys/class/power_supply/*/current_avg
  • /sys/class/power_supply/*/current_max
  • /sys/class/power_supply/*/current_now
  • /sys/class/power_supply/*/cycle_count
  • /sys/class/power_supply/*/health
  • /sys/class/power_supply/*/online
  • /sys/class/power_supply/*/present
  • /sys/class/power_supply/*/status
  • /sys/class/power_supply/*/technology
  • /sys/class/power_supply/*/temp
  • /sys/class/power_supply/*/time_to_full_now (added in health 2.1)
  • /sys/class/power_supply/*/type
  • /sys/class/power_supply/*/voltage_max
  • /sys/class/power_supply/*/voltage_now

Any device-specific health HAL implementation that uses libbatterymonitor accesses these kernel interfaces by default, unless overridden in the health implementation class constructor.

If these files are missing or are inaccessible from healthd or from the default service (e.g. the file is a symlink to a vendor-specific folder that denies access because of misconfigured SELinux policy), they may not function correctly. Hence, additional vendor-specific SELinux changes may be necessary even though the default implementation is used.

Some kernel interfaces used in Health 2.1, such as /sys/class/power_supply/*/capacity_level and /sys/class/power_supply/*/time_to_full_now, may be optional. However, to prevent incorrect framework behaviors resulting from missing kernel interfaces, it is recommended to cherry-pick CL 1398913 before building the health HAL 2.1 service.

Testing

Android 11 includes new VTS tests written specifically for the health@2.1 HAL. If a device declares health@2.1 HAL in the device manifest, it must pass the corresponding VTS tests. Tests are written for both the default instance (to ensure that the device implements the HAL correctly) and the backup instance (to ensure that healthd continues to function correctly before it is removed).

Battery information requirements

The health 2.0 HAL states a set of requirements on the HAL interface, but the corresponding VTS tests are relatively relaxed on enforcing them. In Android 11, new VTS tests are added to enforce the following requirements on devices launching with Android 11 and higher:

  • The units of intataneous and average battery current must be microamps (μA).
  • The sign of instantaneous and average battery current must be correct. Specifically:
    • current == 0 when battery status is UNKNOWN
    • current > 0 when battery status is CHARGING
    • current <= 0 when battery status is NOT_CHARGING
    • current < 0 when battery status is DISCHARGING
    • Not enforced when battery status is FULL
  • The battery status must be correct against whether or not a power source is connected. Specifically:
    • battery status must be one of CHARGING, NOT_CHARGING, or FULL if and only if a power source is connected;
    • battery status must be DISCHARGING if and only if a power source is disconnected.

If you use libbatterymonitor in your implementation and pass through values from kernel interfaces, ensure the sysfs nodes are reporting correct values:

  • Ensure the battery current is reported with the correct sign and units. This includes the following sysfs nodes:
    • /sys/class/power_supply/*/current_avg
    • /sys/class/power_supply/*/current_max
    • /sys/class/power_supply/*/current_now
    • Positive values indicate incoming current into the battery.
    • Values should be in microamps (μA).
  • Ensure the battery voltage is reported in microvolts (μV). This includes the following sysfs nodes:
    • /sys/class/power_supply/*/voltage_max
    • /sys/class/power_supply/*/voltage_now
    • Note that the default HAL implementation divides voltage_now by 1000 and reports values in millivolts (mV). See @1.0::HealthInfo.

For details, see Linux power supply class.