You need a custom implementation if any of the following is true:
ro.charger.enable_suspend
and/or ro.charger.no_ui
are set to a true
value. See below.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.
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.
service
declaration for charger in init.rc
{#charger-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:
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.<name>=running
where <name>
is the name of your charger service, then you need a custom health AIDL service.charger
, you need a custom health AIDL service.seclabel
line. Replace charger
with charger_vendor
.user
(not system
), you need a custom health AIDL service.group
s beside system wakelock input
, you need a custom health AIDL service.SYS_BOOT
, you need a custom health AIDL service.file
s to be opened prior to execution, you need a custom health AIDL 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 \ 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 already contains an entry for charger.
If your device supports charger mode and it has custom charger resources, move charger resources to /vendor
Health
class {#health-impl}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 |
main()
{#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()
.
Install both the platform and recovery variant of the service. For example:
PRODUCT_PACKAGES += \ android.hardware.health-service.cuttlefish \ android.hardware.health-service.cuttlefish_recovery \
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
:
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/<manufacturer>/<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.
# allow hal_health_default ...;
/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
init.rc
for chargerIt 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.<name>=running
, where <name>
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 for an example:
service vendor.charger /vendor/bin/hw/android.hardware.health-service-tuna --charger class charger seclabel u:r:charger_vendor:s0 # ...
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.
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(); }
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 } // ... }
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
:
domain_trans(init, hal_health_tuna_exec, charger_vendor)