# Implementing Health 2.1 HAL 1. Install common binderized service. The binderized service `dlopen()`s passthrough implementations on the device, so there is no need to write your own. ```mk # Install default binderized implementation to vendor. PRODUCT_PACKAGES += android.hardware.health@2.1-service ``` 1. Delete existing VINTF manifest entry. Search for `android.hardware.health` in your device manifest, and delete the whole `` entry for older versions of the HAL. Instead, when `android.hardware.health@2.1-service` is installed, a VINTF manifest fragment is installed to `/vendor/etc/vintf`, so there is no need to manually specify it in your device manifest. See [Manifest fragments](https://source.android.com/devices/architecture/vintf/objects#manifest-fragments) for details. 1. Install the proper passthrough implemetation. 1. If you want to use default implementation: ```mk # Install default passthrough implementation to vendor. PRODUCT_PACKAGES += android.hardware.health@2.1-impl # For non-A/B devices, install default passthrough implementation to recovery. PRODUCT_PACKAGES += android.hardware.health@2.1-impl.recovery ``` You are done. Otherwise, go to the next step. 1. If you want to write your own implementation, 1. Copy skeleton implementation from the [appendix](#impl). 1. Modify the implementation to suit your needs. * If you have a board or device specific `libhealthd`, see [Upgrading with a customized libhealthd](#update-from-1-0). * If you are upgrading from 1.0 health HAL, see [Upgrading from Health HAL 1.0](#update-from-1-0). * If you are upgrading from a customized 2.0 health HAL implementation, See [Upgrading from Health HAL 2.0](#update-from-2-0). 1. [Install the implementation](#install). 1. [Update necessary SELinux permissions](#selinux). 1. [Fix `/charger` symlink](#charger-symlink). # Upgrading with a customized libhealthd or from Health HAL 1.0 {#update-from-1-0} `libhealthd` contains two functions: `healthd_board_init()` and `healthd_board_battery_update()`. Similarly, Health HAL 1.0 contains `init()` and `update()`, with an additional `energyCounter()` function. * `healthd_board_init()` / `@1.0::IHealth.init()` should be called before passing the `healthd_config` struct to your `HealthImpl` class. See `HIDL_FETCH_IHealth` in [`HealthImpl.cpp`](#health_impl_cpp). * `healthd_board_battery_update()` / `@1.0::IHealth.update()` should be called in `HealthImpl::UpdateHealthInfo()`. Example: ```c++ void HealthImpl::UpdateHealthInfo(HealthInfo* health_info) { struct BatteryProperties props; convertFromHealthInfo(health_info->legacy.legacy, &props); healthd_board_battery_update(&props); convertToHealthInfo(&props, health_info->legacy.legacy); } ``` For efficiency, you should move code in `healthd_board_battery_update` to `HealthImpl::UpdateHealthInfo` and modify `health_info` directly to avoid conversion to `BatteryProperties`. * Code for `@1.0::IHealth.energyCounter()` should be moved to `HealthImpl::getEnergyCounter()`. Example: ```c++ Return Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) { int64_t energy = /* ... */; _hidl_cb(Result::SUCCESS, energy); return Void(); } ``` # Upgrading from Health HAL 2.0 {#update-from-2-0} * If you have implemented `healthd_board_init()` and/or `healthd_board_battery_update()` (instead of using `libhealthd.default`), see [the section above](#update-from-1-0) for instructions to convert them. * If you have implemented `get_storage_info()` and/or `get_disk_stats()` (instead of using libhealthstoragedefault), implement `HealthImpl::getDiskStats` and/or `HealthImpl::getStorageInfo` directly. There is no need to override `HealthImpl::getHealthInfo` or `HealthImpl::getHealthInfo_2_1` because they call `getDiskStats` and `getStorageInfo` to retrieve storage information. # Install the implementation {#install} In `device.mk`: ```mk # Install the passthrough implementation to vendor. PRODUCT_PACKAGES += android.hardware.health@2.1-impl- # For non-A/B devices, also install the passthrough implementation to recovery. PRODUCT_PACKAGES += android.hardware.health@2.1-impl-.recovery ``` # Update necessary SELinux permissions {#selinux} For example (replace `` with the device name): ``` # device///sepolicy/vendor/hal_health_default.te # Add device specific permissions to hal_health_default domain, especially # if a device-specific libhealthd is used and/or device-specific storage related # APIs are implemented. ``` # Fix `/charger` symlink {#charger-symlink} If you are using `/charger` in your `init.rc` scripts, it is recommended (required for devices running in Android R) that the path is changed to `/system/bin/charger` instead. Search for `service charger` in your device configuration directory to see if this change applies to your device. Below is an example of how the script should look like: ``` service charger /system/bin/charger class charger user system group system wakelock input capabilities SYS_BOOT file /dev/kmsg w file /sys/fs/pstore/console-ramoops-0 r file /sys/fs/pstore/console-ramoops r file /proc/last_kmsg r ``` # Appendix: sample code for the implementation {#impl} ## `device///health/Android.bp` {#android_bp} ```bp cc_library_shared { name: "android.hardware.health@2.1-impl-", stem: "android.hardware.health@2.0-impl-2.1-", // Install to vendor and recovery. proprietary: true, recovery_available: true, relative_install_path: "hw", shared_libs: [ "libbase", "libcutils", "libhidlbase", "liblog", "libutils", "android.hardware.health@2.1", "android.hardware.health@2.0", ], static_libs: [ "android.hardware.health@1.0-convert", "libbatterymonitor", "libhealthloop", "libhealth2impl", // "libhealthd." ], srcs: [ "HealthImpl.cpp", ], // No vintf_fragments because both -impl and -service should have been // installed. } ``` ## `device///health/HealthImpl.cpp` {#health_impl_cpp} ```c++ #include #include #include #include #include using ::android::sp; using ::android::hardware::Return; using ::android::hardware::Void; using ::android::hardware::health::InitHealthdConfig; using ::android::hardware::health::V2_1::IHealth; using ::android::hidl::base::V1_0::IBase; using namespace std::literals; namespace android { namespace hardware { namespace health { namespace V2_1 { namespace implementation { // android::hardware::health::V2_1::implementation::Health implements most // defaults. Uncomment functions that you need to override. class HealthImpl : public Health { public: HealthImpl(std::unique_ptr&& config) : Health(std::move(config)) {} // A subclass can override this if these information should be retrieved // differently. // Return getChargeCounter(getChargeCounter_cb _hidl_cb) override; // Return getCurrentNow(getCurrentNow_cb _hidl_cb) override; // Return getCurrentAverage(getCurrentAverage_cb _hidl_cb) override; // Return getCapacity(getCapacity_cb _hidl_cb) override; // Return getEnergyCounter(getEnergyCounter_cb _hidl_cb) override; // Return getChargeStatus(getChargeStatus_cb _hidl_cb) override; // Return getStorageInfo(getStorageInfo_cb _hidl_cb) override; // Return getDiskStats(getDiskStats_cb _hidl_cb) override; // Return getHealthInfo(getHealthInfo_cb _hidl_cb) override; // Functions introduced in Health HAL 2.1. // Return getHealthConfig(getHealthConfig_cb _hidl_cb) override; // Return getHealthInfo_2_1(getHealthInfo_2_1_cb _hidl_cb) override; // Return shouldKeepScreenOn(shouldKeepScreenOn_cb _hidl_cb) override; protected: // A subclass can override this to modify any health info object before // returning to clients. This is similar to healthd_board_battery_update(). // By default, it does nothing. // void UpdateHealthInfo(HealthInfo* health_info) override; }; } // namespace implementation } // namespace V2_1 } // namespace health } // namespace hardware } // namespace android extern "C" IHealth* HIDL_FETCH_IHealth(const char* instance) { using ::android::hardware::health::V2_1::implementation::HealthImpl; if (instance != "default"sv) { return nullptr; } auto config = std::make_unique(); InitHealthdConfig(config.get()); // healthd_board_init(config.get()); return new HealthImpl(std::move(config)); } ```