platform_hardware_interfaces/health/aidl/README.md
Yifan Hong 02f04175c9 health AIDL: Add readme
Test: none
Bug: 177269435
Change-Id: I54e69d7789c6e1246bb126a27f9463e04407314e
2021-11-08 12:28:18 -08:00

10 KiB

Health AIDL HAL

Determine whether the example service implementation is sufficient

You need a custom implementation if any of the following is true:

  • You are migrating from a custom health 2.1 HIDL HAL implementation.
  • System properties ro.charger.enable_suspend and/or ro.charger.no_ui are set to a true value. See below.
  • 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.

If the example HAL service is sufficient, install it. Otherwise, implement a custom HAL service.

System properties for charger

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.
  • ro.charger.no_ui. If set, you need a custom health AIDL HAL service. See below.
  • 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.

Default service declaration for charger in init.rc

See 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):

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. Specifically:

  • You may ignore the service line. The name of the service does not matter.
  • If your service belongs to additional classes beside charger, you need a custom health AIDL service.
  • You may ignore the seclabel line. When the health AIDL service runs in charger mode, its original SELinux domain is kept.
  • If your service has a different user (not system), you need a custom health AIDL service.
  • If your service belongs to additional groups 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 files to be opened prior to execution, you need a custom health AIDL service.

Using the example health AIDL HAL service

If you determined that the example health AIDL HAL service works for your device, install it with

PRODUCT_PACKAGES += android.hardware.health-service.example

Then, delete any existing service with class charger in your device-specific init.rc files, because 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

Implementing a custom health AIDL HAL service

Override the Health class

See Health.h for its class declaration. Inherit the class to customize for your device.

namespace aidl::android::hardware::health {
class HealthImpl : public Health {
    // ...
};
} // namespace aidl::android::hardware::health
int main(int, char**) {
    // ...
    auto binder = ndk::SharedRefBase::make<aidl::android::hardware::health::HealthImpl>(
            "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().

  • 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()

See the 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).

int main(int argc, char** argv) {
    auto config = std::make_unique<healthd_config>();
    ::android::hardware::health::InitHealthdConfig(config.get());
    healthd_board_init(config.get());
    auto binder = ndk::SharedRefBase::make<Health>("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().

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.

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/<manufacturer>/<device>/sepolicy/vendor/hal_health_default.te.

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

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

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.

Modify the entry to invoke the health service binary with --charger argument. See android.hardware.health-service.example.rc for an example:

service vendor.charger-tuna /vendor/bin/hw/android.hardware.health-service-tuna --charger
    # ...

No charger mode

If your device does not support off-line charging mode, skip the invocation of ChargerModeMain() in main().

int main(int, char**) {
    // ...
    // Skip checking if arguments contain "--charger"
    auto hal_health_loop = std::make_shared<HalHealthLoop>(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

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.

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<HalHealthLoop>(binder, binder);
    return hal_health_loop->StartLoop();
}

Enable suspend

If your device has ro.charger.enable_suspend=true, implement a new class, ChargerCallbackImpl, that inherits from ChargerCallback. Then override the ChargerEnableSuspend function to return true. Then pass an instance of ChargerCallbackImpl to ChargerModeMain() instead.

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<aidl::android::hardware::health::ChargerCallbackImpl>(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:

type hal_health_tuna, charger_type, domain;
hal_server_domain(hal_health_default, hal_health)