1# Health AIDL HAL
2
3## Determine whether the example service implementation is sufficient {#determine}
4
5You need a custom implementation if any of the following is true:
6
7* You are migrating from a custom
8  [health 2.1 HIDL HAL implementation](../2.1/README.md).
9* System properties `ro.charger.enable_suspend` and/or `ro.charger.no_ui`
10  are set to a `true` value. See [below](#charger-sysprops).
11* The device supports offline charging mode, and the `service`
12  declaration with `class charger` in `init.rc` is different from the one
13  provided by the example implementation. See [below](#charger-init-rc).
14
15If the example HAL service is sufficient, [install it](#use-example). Otherwise,
16[implement a custom HAL service](#use-custom).
17
18### System properties for charger {#charger-sysprops}
19
20The health AIDL HAL service also provides functionalities of `charger`. As a
21result, the system charger at `/system/bin/charger` is deprecated.
22
23However, the health AIDL HAL service is not allowed to read `ro.charger.*`
24system properties. These properties include:
25* `ro.charger.enable_suspend`. If set, you need a custom health AIDL HAL
26  service. See [below](#charger-enable-suspend).
27* `ro.charger.no_ui`. If set, you need a custom health AIDL HAL service.
28  See [below](#charger-no-ui).
29* `ro.charger.draw_split_screen`. The system property is deprecated.
30* `ro.charger.draw_split_offset`. The system property is deprecated.
31* `ro.charger.disable_init_blank`. The system property is deprecated.
32
33If you need to set any of the deprecated system properties, contact
34[OWNERS](OWNERS).
35
36### Default `service` declaration for charger in `init.rc` {#charger-init-rc}
37
38See
39[android.hardware.health-service.example.rc](default/android.hardware.health-service.example.rc).
40
41Check the `service` declaration in your device-specific `init.rc` file that
42has `class charger`. Most likely, the declaration looks something like this
43(Below is an excerpt from Pixel 3):
44
45```text
46service vendor.charger /system/bin/charger
47    class charger
48    seclabel u:r:charger:s0
49    user system
50    group system wakelock input
51    capabilities SYS_BOOT
52    file /dev/kmsg w
53    file /sys/fs/pstore/console-ramoops-0 r
54    file /sys/fs/pstore/console-ramoops r
55    file /proc/last_kmsg r
56```
57
58Compare each line against the one provided by the example health AIDL HAL
59service in
60[android.hardware.health-service.example.rc](default/android.hardware.health-service.example.rc).
61Specifically:
62
63* For the `service` line, if the name of the service is **NOT**
64  `vendor.charger`, and there are actions
65  in the rc file triggered by `on property:init.svc.<name>=running` where
66  `<name>` is the name of your charger service, then you need a custom health
67  AIDL service.
68* If your service belongs to additional classes beside `charger`, you need a
69  custom health AIDL service.
70* Modify the `seclabel` line. Replace `charger` with `charger_vendor`.
71* If your service has a different `user` (not `system`), you need a custom
72  health AIDL service.
73* If your service belongs to additional `group`s beside
74  `system wakelock input`, you need a custom health AIDL service.
75* If your service requires additional capabilities beside `SYS_BOOT`,
76  you need a custom health AIDL service.
77* If your service requires additional `file`s to be opened prior to execution,
78  you need a custom health AIDL service.
79
80## Using the example health AIDL HAL service {#use-example}
81
82If you [determined](#determine) that the example health AIDL HAL service works
83for your device, install it with
84
85```mk
86PRODUCT_PACKAGES += \
87    android.hardware.health-service.example \
88    android.hardware.health-service.example_recovery \
89```
90
91Then, delete any existing `service` with `class charger` in your device-specific
92`init.rc` files, because
93[android.hardware.health-service.example.rc](default/android.hardware.health-service.example.rc)
94already contains an entry for charger.
95
96If your device supports charger mode and it has custom charger resources,
97[move charger resources to `/vendor`](#charger-res)
98
99## Implementing a custom health AIDL HAL service {#use-custom}
100
101### Override the `Health` class {#health-impl}
102
103See [`Health.h`](default/include/health-impl/Health.h) for its class
104declaration. Inherit the class to customize for your device.
105
106```c++
107namespace aidl::android::hardware::health {
108class HealthImpl : public Health {
109    // ...
110};
111} // namespace aidl::android::hardware::health
112int main(int, char**) {
113    // ...
114    auto binder = ndk::SharedRefBase::make<aidl::android::hardware::health::HealthImpl>(
115            "default", std::move(config));
116    // ...
117}
118```
119
120* The logic to modify `healthd_config`, traditionally in `healthd_board_init()`
121  should be called before passing the `healthd_config` struct to your
122  `HealthImpl` class in [`main()`](#main).
123
124* The following functions are similar to the ones in the health 2.1 HIDL HAL:
125
126| AIDL implementation                 | HIDL implementation         |
127|-------------------------------------|-----------------------------|
128| `Health::getChargeCounterUah`       | `Health::getChargeCounter`  |
129| `Health::getCurrentNowMicroamps`    | `Health::getCurrentNow`     |
130| `Health::getCurrentAverageMicroamps`| `Health::getCurrentAverage` |
131| `Health::getCapacity`               | `Health::getCapacity`       |
132| `Health::getChargeStatus`           | `Health::getChargeStatus`   |
133| `Health::getEnergyCounterNwh`       | `Health::getEnergyCounter`  |
134| `Health::getDiskStats`              | `Health::getDiskStats`      |
135| `Health::getStorageInfo`            | `Health::getStorageInfo`    |
136| `Health::BinderEvent`               | `BinderHealth::BinderEvent` |
137| `Health::dump`                      | `Health::debug`             |
138| `Health::ShouldKeepScreenOn`        | `Health::shouldKeepScreenOn`|
139| `Health::UpdateHealthInfo`          | `Health::UpdateHealthInfo`  |
140
141### Implement `main()` {#main}
142
143See the [`main.cpp`](default/main.cpp) for the example health AIDL service for
144an example.
145
146If you need to modify `healthd_config`, do it before passing it to the
147constructor of `HealthImpl` (or `Health` if you did not implement a subclass
148of it).
149
150```c++
151int main(int argc, char** argv) {
152    auto config = std::make_unique<healthd_config>();
153    ::android::hardware::health::InitHealthdConfig(config.get());
154    healthd_board_init(config.get());
155    auto binder = ndk::SharedRefBase::make<Health>("default", std::move(config));
156    // ...
157}
158```
159
160If your device does not support off-line charging mode, or does not have a UI
161for charger (`ro.charger.no_ui=true`), skip the invocation of
162`ChargerModeMain()` in `main()`.
163
164### Build system changes
165
166Install both the platform and recovery variant of the service. For example:
167
168```mk
169PRODUCT_PACKAGES += \
170    android.hardware.health-service.cuttlefish \
171    android.hardware.health-service.cuttlefish_recovery \
172```
173
174### SELinux rules
175
176Add device specific permissions to the domain where the health HAL
177process is executed, especially if a device-specific `libhealthd` is used
178and/or device-specific storage related APIs are implemented.
179
180Example (assuming that your health AIDL service runs in domain
181`hal_health_tuna`:
182
183```text
184type hal_health_tuna, domain;
185hal_server_domain(hal_health_tuna, hal_health)
186type hal_health_tuna_exec, exec_type, vendor_file_type, file_type;
187
188# allow hal_health_tuna ...;
189```
190
191If you did not define a separate domain, the domain is likely
192`hal_health_default`. The device-specific rules for it is likely at
193`device/<manufacturer>/<device>/sepolicy/vendor/hal_health_default.te`.
194In this case, the aforementioned SELinux rules and types has already been
195defined. You only need to add device-specific permissions.
196
197```text
198# allow hal_health_default ...;
199```
200
201### Implementing charger {#charger}
202
203#### Move charger resources to `/vendor`
204
205Ensure that charger resources are installed to `/vendor`, not `/product`.
206
207`animation.txt` must be moved to the following location:
208
209```text
210/vendor/etc/res/values/charger/animation.txt
211```
212
213Charger resources in `/system` is not read by the health HAL service in
214`/vendor`. Specifically, resources should be installed to the following
215location:
216
217```
218/vendor/etc/res/images/charger/*.png
219```
220
221If resources are not found in these locations, the health HAL service falls
222back to the following locations:
223
224```
225/vendor/etc/res/images/charger/default/*.png
226```
227
228You can use the default resources by installing the default module:
229
230```makefile
231PRODUCT_PACKAGES += charger_res_images_vendor
232```
233
234#### Modify `init.rc` for charger
235
236It is recommended that you move the existing `service` entry with
237`class charger` to the `init.rc` file in your custom health service.
238
239If there are existing actions in the rc file triggered by
240`on property:init.svc.<name>=running`, where `<name>` is the name of your
241existing charger service (usually `vendor.charger`), then the name of the
242service must be kept as-is. If you modify the name of the service, the actions
243are not triggered properly.
244
245Modify the entry to invoke the health service binary with `--charger` argument.
246See
247[android.hardware.health-service.example.rc](default/android.hardware.health-service.example.rc)
248for an example:
249
250```text
251service vendor.charger /vendor/bin/hw/android.hardware.health-service-tuna --charger
252    class charger
253    seclabel u:r:charger_vendor:s0
254    # ...
255```
256
257#### No charger mode {#no-charger}
258
259If your device does not support off-line charging mode, skip the invocation of
260`ChargerModeMain()` in `main()`.
261
262```c++
263int main(int, char**) {
264    // ...
265    // Skip checking if arguments contain "--charger"
266    auto hal_health_loop = std::make_shared<HalHealthLoop>(binder, binder);
267    return hal_health_loop->StartLoop();
268}
269```
270
271You may optionally delete the `service` entry with `class charger` in the
272`init.rc` file.
273
274#### No charger UI {#charger-no-ui}
275
276If your device does not have a UI for charger (`ro.charger.no_ui=true`), skip
277the invocation of `ChargerModeMain()` in `main()`.
278
279You may want to keep the `KernelLogger` so that charger still logs battery
280information to the kernel logs.
281
282```c++
283int main(int argc, char** argv) {
284    // ...
285    if (argc >= 2 && argv[1] == "--charger"sv) {
286        android::base::InitLogging(argv, &android::base::KernelLogger);
287        // fallthrough to HalHealthLoop::StartLoop()
288    }
289    auto hal_health_loop = std::make_shared<HalHealthLoop>(binder, binder);
290    return hal_health_loop->StartLoop();
291}
292```
293
294#### Enable suspend {#charger-enable-suspend}
295
296If your device has `ro.charger.enable_suspend=true`, implement a new class,
297`ChargerCallbackImpl`, that inherits from
298[`ChargerCallback`](default/include/health-impl/ChargerUtils.h). Then
299override the `ChargerEnableSuspend` function to return `true`. Then pass an
300instance of `ChargerCallbackImpl` to `ChargerModeMain()` instead.
301
302```c++
303namespace aidl::android::hardware::health {
304class ChargerCallbackImpl : public ChargerCallback {
305    bool ChargerEnableSuspend() override { return true; }
306};
307} // namespace aidl::android::hardware::health
308int main(int argc, char** argv) {
309    // ...
310    if (argc >= 2 && argv[1] == "--charger"sv) {
311        android::base::InitLogging(argv, &android::base::KernelLogger);
312#if !CHARGER_FORCE_NO_UI
313        return ChargerModeMain(binder,
314                std::make_shared<aidl::android::hardware::health::ChargerCallbackImpl>(binder));
315#endif
316    }
317    // ...
318}
319```
320
321#### SELinux rules for charger
322
323If your health AIDL service runs in a domain other than `hal_health_default`,
324add `charger_type` to it so the health HAL service can have charger-specific
325permissions. Example (assuming that your health AIDL service runs in domain
326`hal_health_tuna`:
327
328```text
329domain_trans(init, hal_health_tuna_exec, charger_vendor)
330```
331