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)