# Health AIDL HAL ## Determine whether the example service implementation is sufficient {#determine} You need a custom implementation if any of the following is true: * You are migrating from a custom [health 2.1 HIDL HAL implementation](../2.1/README.md). * System properties `ro.charger.enable_suspend` and/or `ro.charger.no_ui` are set to a `true` value. See [below](#charger-sysprops). * The device supports offline charging mode, and the `service` declaration with `class charger` in `init.rc` is different from the one provided by the example implementation. See [below](#charger-init-rc). If the example HAL service is sufficient, [install it](#use-example). Otherwise, [implement a custom HAL service](#use-custom). ### System properties for charger {#charger-sysprops} The health AIDL HAL service also provides functionalities of `charger`. As a result, the system charger at `/system/bin/charger` is deprecated. However, the health AIDL HAL service is not allowed to read `ro.charger.*` system properties. These properties include: * `ro.charger.enable_suspend`. If set, you need a custom health AIDL HAL service. See [below](#charger-enable-suspend). * `ro.charger.no_ui`. If set, you need a custom health AIDL HAL service. See [below](#charger-no-ui). * `ro.charger.draw_split_screen`. The system property is deprecated. * `ro.charger.draw_split_offset`. The system property is deprecated. * `ro.charger.disable_init_blank`. The system property is deprecated. If you need to set any of the deprecated system properties, contact [OWNERS](OWNERS). ### Default `service` declaration for charger in `init.rc` {#charger-init-rc} See [android.hardware.health-service.example.rc](default/android.hardware.health-service.example.rc). Check the `service` declaration in your device-specific `init.rc` file that has `class charger`. Most likely, the declaration looks something like this (Below is an excerpt from Pixel 3): ```text service vendor.charger /system/bin/charger class charger seclabel u:r:charger:s0 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 ``` Compare each line against the one provided by the example health AIDL HAL service in [android.hardware.health-service.example.rc](default/android.hardware.health-service.example.rc). Specifically: * For the `service` line, if the name of the service is **NOT** `vendor.charger`, and there are actions in the rc file triggered by `on property:init.svc.=running` where `` is the name of your charger service, then you need a custom health AIDL service. * If your service belongs to additional classes beside `charger`, you need a custom health AIDL service. * Modify the `seclabel` line. Replace `charger` with `charger_vendor`. * If your service has a different `user` (not `system`), you need a custom health AIDL service. * If your service belongs to additional `group`s beside `system wakelock input`, you need a custom health AIDL service. * If your service requires additional capabilities beside `SYS_BOOT`, you need a custom health AIDL service. * If your service requires additional `file`s to be opened prior to execution, you need a custom health AIDL service. ## Using the example health AIDL HAL service {#use-example} If you [determined](#determine) that the example health AIDL HAL service works for your device, install it with ```mk PRODUCT_PACKAGES += \ android.hardware.health-service.example \ android.hardware.health-service.example_recovery \ ``` Then, delete any existing `service` with `class charger` in your device-specific `init.rc` files, because [android.hardware.health-service.example.rc](default/android.hardware.health-service.example.rc) already contains an entry for charger. If your device supports charger mode and it has custom charger resources, [move charger resources to `/vendor`](#charger-res) ## Implementing a custom health AIDL HAL service {#use-custom} ### Override the `Health` class {#health-impl} See [`Health.h`](default/include/health-impl/Health.h) for its class declaration. Inherit the class to customize for your device. ```c++ namespace aidl::android::hardware::health { class HealthImpl : public Health { // ... }; } // namespace aidl::android::hardware::health int main(int, char**) { // ... auto binder = ndk::SharedRefBase::make( "default", std::move(config)); // ... } ``` * The logic to modify `healthd_config`, traditionally in `healthd_board_init()` should be called before passing the `healthd_config` struct to your `HealthImpl` class in [`main()`](#main). * The following functions are similar to the ones in the health 2.1 HIDL HAL: | AIDL implementation | HIDL implementation | |-------------------------------------|-----------------------------| | `Health::getChargeCounterUah` | `Health::getChargeCounter` | | `Health::getCurrentNowMicroamps` | `Health::getCurrentNow` | | `Health::getCurrentAverageMicroamps`| `Health::getCurrentAverage` | | `Health::getCapacity` | `Health::getCapacity` | | `Health::getChargeStatus` | `Health::getChargeStatus` | | `Health::getEnergyCounterNwh` | `Health::getEnergyCounter` | | `Health::getDiskStats` | `Health::getDiskStats` | | `Health::getStorageInfo` | `Health::getStorageInfo` | | `Health::BinderEvent` | `BinderHealth::BinderEvent` | | `Health::dump` | `Health::debug` | | `Health::ShouldKeepScreenOn` | `Health::shouldKeepScreenOn`| | `Health::UpdateHealthInfo` | `Health::UpdateHealthInfo` | ### Implement `main()` {#main} See the [`main.cpp`](default/main.cpp) for the example health AIDL service for an example. If you need to modify `healthd_config`, do it before passing it to the constructor of `HealthImpl` (or `Health` if you did not implement a subclass of it). ```c++ int main(int argc, char** argv) { auto config = std::make_unique(); ::android::hardware::health::InitHealthdConfig(config.get()); healthd_board_init(config.get()); auto binder = ndk::SharedRefBase::make("default", std::move(config)); // ... } ``` If your device does not support off-line charging mode, or does not have a UI for charger (`ro.charger.no_ui=true`), skip the invocation of `ChargerModeMain()` in `main()`. ### Build system changes Install both the platform and recovery variant of the service. For example: ```mk PRODUCT_PACKAGES += \ android.hardware.health-service.cuttlefish \ android.hardware.health-service.cuttlefish_recovery \ ``` ### SELinux rules Add device specific permissions to the domain where the health HAL process is executed, especially if a device-specific `libhealthd` is used and/or device-specific storage related APIs are implemented. Example (assuming that your health AIDL service runs in domain `hal_health_tuna`: ```text type hal_health_tuna, domain; hal_server_domain(hal_health_tuna, hal_health) type hal_health_tuna_exec, exec_type, vendor_file_type, file_type; # allow hal_health_tuna ...; ``` If you did not define a separate domain, the domain is likely `hal_health_default`. The device-specific rules for it is likely at `device///sepolicy/vendor/hal_health_default.te`. In this case, the aforementioned SELinux rules and types has already been defined. You only need to add device-specific permissions. ```text # allow hal_health_default ...; ``` ### Implementing charger {#charger} #### Move charger resources to `/vendor` Ensure that charger resources are installed to `/vendor`, not `/product`. `animation.txt` must be moved to the following location: ```text /vendor/etc/res/values/charger/animation.txt ``` Charger resources in `/system` is not read by the health HAL service in `/vendor`. Specifically, resources should be installed to the following location: ``` /vendor/etc/res/images/charger/*.png ``` If resources are not found in these locations, the health HAL service falls back to the following locations: ``` /vendor/etc/res/images/charger/default/*.png ``` You can use the default resources by installing the default module: ```makefile PRODUCT_PACKAGES += charger_res_images_vendor ``` #### Modify `init.rc` for charger It is recommended that you move the existing `service` entry with `class charger` to the `init.rc` file in your custom health service. If there are existing actions in the rc file triggered by `on property:init.svc.=running`, where `` is the name of your existing charger service (usually `vendor.charger`), then the name of the service must be kept as-is. If you modify the name of the service, the actions are not triggered properly. Modify the entry to invoke the health service binary with `--charger` argument. See [android.hardware.health-service.example.rc](default/android.hardware.health-service.example.rc) for an example: ```text service vendor.charger /vendor/bin/hw/android.hardware.health-service-tuna --charger class charger seclabel u:r:charger_vendor:s0 # ... ``` #### No charger mode {#no-charger} If your device does not support off-line charging mode, skip the invocation of `ChargerModeMain()` in `main()`. ```c++ int main(int, char**) { // ... // Skip checking if arguments contain "--charger" auto hal_health_loop = std::make_shared(binder, binder); return hal_health_loop->StartLoop(); } ``` You may optionally delete the `service` entry with `class charger` in the `init.rc` file. #### No charger UI {#charger-no-ui} If your device does not have a UI for charger (`ro.charger.no_ui=true`), skip the invocation of `ChargerModeMain()` in `main()`. You may want to keep the `KernelLogger` so that charger still logs battery information to the kernel logs. ```c++ int main(int argc, char** argv) { // ... if (argc >= 2 && argv[1] == "--charger"sv) { android::base::InitLogging(argv, &android::base::KernelLogger); // fallthrough to HalHealthLoop::StartLoop() } auto hal_health_loop = std::make_shared(binder, binder); return hal_health_loop->StartLoop(); } ``` #### Enable suspend {#charger-enable-suspend} If your device has `ro.charger.enable_suspend=true`, implement a new class, `ChargerCallbackImpl`, that inherits from [`ChargerCallback`](default/include/health-impl/ChargerUtils.h). Then override the `ChargerEnableSuspend` function to return `true`. Then pass an instance of `ChargerCallbackImpl` to `ChargerModeMain()` instead. ```c++ namespace aidl::android::hardware::health { class ChargerCallbackImpl : public ChargerCallback { bool ChargerEnableSuspend() override { return true; } }; } // namespace aidl::android::hardware::health int main(int argc, char** argv) { // ... if (argc >= 2 && argv[1] == "--charger"sv) { android::base::InitLogging(argv, &android::base::KernelLogger); #if !CHARGER_FORCE_NO_UI return ChargerModeMain(binder, std::make_shared(binder)); #endif } // ... } ``` #### SELinux rules for charger If your health AIDL service runs in a domain other than `hal_health_default`, add `charger_type` to it so the health HAL service can have charger-specific permissions. Example (assuming that your health AIDL service runs in domain `hal_health_tuna`: ```text domain_trans(init, hal_health_tuna_exec, charger_vendor) ```