Yifan Hong | e438211 | 2019-10-02 19:09:37 -0700 | [diff] [blame] | 1 | # Implementing Health 2.1 HAL |
| 2 | |
| 3 | 1. Install common binderized service. The binderized service `dlopen()`s |
| 4 | passthrough implementations on the device, so there is no need to write |
| 5 | your own. |
| 6 | |
| 7 | ```mk |
| 8 | # Install default binderized implementation to vendor. |
| 9 | PRODUCT_PACKAGES += [email protected] |
| 10 | ``` |
| 11 | |
| 12 | 1. Delete existing VINTF manifest entry. Search for `android.hardware.health` in |
| 13 | your device manifest, and delete the whole `<hal>` entry for older versions |
| 14 | of the HAL. Instead, when `[email protected]` is installed, |
| 15 | a VINTF manifest fragment is installed to `/vendor/etc/vintf`, so there is |
| 16 | no need to manually specify it in your device manifest. See |
| 17 | [Manifest fragments](https://source.android.com/devices/architecture/vintf/objects#manifest-fragments) |
| 18 | for details. |
| 19 | |
| 20 | 1. Install the proper passthrough implemetation. |
| 21 | |
| 22 | 1. If you want to use default implementation: |
| 23 | |
| 24 | ```mk |
| 25 | # Install default passthrough implementation to vendor. |
| 26 | PRODUCT_PACKAGES += [email protected] |
Yifan Hong | b9c4082 | 2020-01-31 17:19:55 -0800 | [diff] [blame] | 27 | |
| 28 | # For non-A/B devices, install default passthrough implementation to recovery. |
| 29 | PRODUCT_PACKAGES += [email protected] |
Yifan Hong | e438211 | 2019-10-02 19:09:37 -0700 | [diff] [blame] | 30 | ``` |
| 31 | |
| 32 | You are done. Otherwise, go to the next step. |
| 33 | |
| 34 | 1. If you want to write your own implementation, |
| 35 | |
| 36 | 1. Copy skeleton implementation from the [appendix](#impl). |
| 37 | |
| 38 | 1. Modify the implementation to suit your needs. |
| 39 | |
| 40 | * If you have a board or device specific `libhealthd`, see |
| 41 | [Upgrading with a customized libhealthd](#update-from-1-0). |
| 42 | * If you are upgrading from 1.0 health HAL, see |
| 43 | [Upgrading from Health HAL 1.0](#update-from-1-0). |
| 44 | * If you are upgrading from a customized 2.0 health HAL |
| 45 | implementation, See |
| 46 | [Upgrading from Health HAL 2.0](#update-from-2-0). |
| 47 | |
Yifan Hong | b9c4082 | 2020-01-31 17:19:55 -0800 | [diff] [blame] | 48 | 1. [Install the implementation](#install). |
| 49 | |
Yifan Hong | e438211 | 2019-10-02 19:09:37 -0700 | [diff] [blame] | 50 | 1. [Update necessary SELinux permissions](#selinux). |
| 51 | |
| 52 | 1. [Fix `/charger` symlink](#charger-symlink). |
| 53 | |
| 54 | # Upgrading with a customized libhealthd or from Health HAL 1.0 {#update-from-1-0} |
| 55 | |
| 56 | `libhealthd` contains two functions: `healthd_board_init()` and |
| 57 | `healthd_board_battery_update()`. Similarly, Health HAL 1.0 contains `init()` |
| 58 | and `update()`, with an additional `energyCounter()` function. |
| 59 | |
| 60 | * `healthd_board_init()` / `@1.0::IHealth.init()` should be called before |
| 61 | passing the `healthd_config` struct to your `HealthImpl` class. See |
| 62 | `HIDL_FETCH_IHealth` in [`HealthImpl.cpp`](#health_impl_cpp). |
| 63 | |
| 64 | * `healthd_board_battery_update()` / `@1.0::IHealth.update()` should be called |
| 65 | in `HealthImpl::UpdateHealthInfo()`. Example: |
| 66 | |
| 67 | ```c++ |
| 68 | void HealthImpl::UpdateHealthInfo(HealthInfo* health_info) { |
| 69 | struct BatteryProperties props; |
| 70 | convertFromHealthInfo(health_info->legacy.legacy, &props); |
| 71 | healthd_board_battery_update(&props); |
| 72 | convertToHealthInfo(&props, health_info->legacy.legacy); |
| 73 | } |
| 74 | ``` |
| 75 | For efficiency, you should move code in `healthd_board_battery_update` to |
| 76 | `HealthImpl::UpdateHealthInfo` and modify `health_info` directly to avoid |
| 77 | conversion to `BatteryProperties`. |
| 78 | |
| 79 | * Code for `@1.0::IHealth.energyCounter()` should be moved to |
| 80 | `HealthImpl::getEnergyCounter()`. Example: |
| 81 | |
| 82 | ```c++ |
| 83 | Return<void> Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) { |
| 84 | int64_t energy = /* ... */; |
| 85 | _hidl_cb(Result::SUCCESS, energy); |
| 86 | return Void(); |
| 87 | } |
| 88 | ``` |
| 89 | |
| 90 | # Upgrading from Health HAL 2.0 {#update-from-2-0} |
| 91 | |
| 92 | * If you have implemented `healthd_board_init()` and/or |
| 93 | `healthd_board_battery_update()` (instead of using `libhealthd.default`), |
| 94 | see [the section above](#update-from-1-0) |
| 95 | for instructions to convert them. |
| 96 | |
| 97 | * If you have implemented `get_storage_info()` and/or `get_disk_stats()` |
| 98 | (instead of using libhealthstoragedefault), implement `HealthImpl::getDiskStats` |
| 99 | and/or `HealthImpl::getStorageInfo` directly. There is no need to override |
| 100 | `HealthImpl::getHealthInfo` or `HealthImpl::getHealthInfo_2_1` because they call |
| 101 | `getDiskStats` and `getStorageInfo` to retrieve storage information. |
| 102 | |
Yifan Hong | b9c4082 | 2020-01-31 17:19:55 -0800 | [diff] [blame] | 103 | # Install the implementation {#install} |
| 104 | |
| 105 | In `device.mk`: |
| 106 | |
| 107 | ```mk |
| 108 | # Install the passthrough implementation to vendor. |
| 109 | PRODUCT_PACKAGES += [email protected]<device> |
| 110 | |
| 111 | # For non-A/B devices, also install the passthrough implementation to recovery. |
| 112 | PRODUCT_PACKAGES += [email protected]<device>.recovery |
| 113 | ``` |
| 114 | |
Yifan Hong | e438211 | 2019-10-02 19:09:37 -0700 | [diff] [blame] | 115 | # Update necessary SELinux permissions {#selinux} |
| 116 | |
| 117 | For example (replace `<device>` with the device name): |
| 118 | ``` |
Yifan Hong | e438211 | 2019-10-02 19:09:37 -0700 | [diff] [blame] | 119 | # device/<manufacturer>/<device>/sepolicy/vendor/hal_health_default.te |
| 120 | # Add device specific permissions to hal_health_default domain, especially |
| 121 | # if a device-specific libhealthd is used and/or device-specific storage related |
| 122 | # APIs are implemented. |
| 123 | ``` |
| 124 | |
| 125 | # Fix `/charger` symlink {#charger-symlink} |
| 126 | If you are using `/charger` in your `init.rc` scripts, it is recommended |
| 127 | (required for devices running in Android R) that the path is changed to |
| 128 | `/system/bin/charger` instead. |
| 129 | |
| 130 | Search for `service charger` in your device configuration directory to see if |
| 131 | this change applies to your device. Below is an example of how the script should |
| 132 | look like: |
| 133 | |
| 134 | ``` |
| 135 | service charger /system/bin/charger |
| 136 | class charger |
| 137 | user system |
| 138 | group system wakelock input |
| 139 | capabilities SYS_BOOT |
| 140 | file /dev/kmsg w |
| 141 | file /sys/fs/pstore/console-ramoops-0 r |
| 142 | file /sys/fs/pstore/console-ramoops r |
| 143 | file /proc/last_kmsg r |
| 144 | ``` |
| 145 | |
| 146 | # Appendix: sample code for the implementation {#impl} |
| 147 | |
| 148 | ## `device/<manufacturer>/<device>/health/Android.bp` {#android_bp} |
| 149 | |
| 150 | ```bp |
| 151 | cc_library_shared { |
| 152 | name: "[email protected]<device>", |
| 153 | stem: "[email protected]<device>", |
| 154 | |
| 155 | // Install to vendor and recovery. |
| 156 | proprietary: true, |
| 157 | recovery_available: true, |
| 158 | |
| 159 | relative_install_path: "hw", |
| 160 | |
| 161 | shared_libs: [ |
| 162 | "libbase", |
| 163 | "libcutils", |
| 164 | "libhidlbase", |
| 165 | "liblog", |
| 166 | "libutils", |
| 167 | "[email protected]", |
| 168 | "[email protected]", |
| 169 | ], |
| 170 | |
| 171 | static_libs: [ |
| 172 | "[email protected]", |
| 173 | "libbatterymonitor", |
| 174 | "libhealthloop", |
| 175 | "libhealth2impl", |
| 176 | // "libhealthd.<device>" |
| 177 | ], |
| 178 | |
| 179 | srcs: [ |
| 180 | "HealthImpl.cpp", |
| 181 | ], |
| 182 | |
| 183 | // No vintf_fragments because both -impl and -service should have been |
| 184 | // installed. |
| 185 | } |
| 186 | ``` |
| 187 | |
| 188 | ## `device/<manufacturer>/<device>/health/HealthImpl.cpp` {#health_impl_cpp} |
| 189 | |
| 190 | ```c++ |
| 191 | #include <memory> |
| 192 | #include <string_view> |
| 193 | |
| 194 | #include <health/utils.h> |
| 195 | #include <health2impl/Health.h> |
| 196 | #include <hidl/Status.h> |
| 197 | |
| 198 | using ::android::sp; |
| 199 | using ::android::hardware::Return; |
| 200 | using ::android::hardware::Void; |
| 201 | using ::android::hardware::health::InitHealthdConfig; |
| 202 | using ::android::hardware::health::V2_1::IHealth; |
| 203 | using ::android::hidl::base::V1_0::IBase; |
| 204 | |
| 205 | using namespace std::literals; |
| 206 | |
| 207 | namespace android { |
| 208 | namespace hardware { |
| 209 | namespace health { |
| 210 | namespace V2_1 { |
| 211 | namespace implementation { |
| 212 | |
| 213 | // android::hardware::health::V2_1::implementation::Health implements most |
| 214 | // defaults. Uncomment functions that you need to override. |
| 215 | class HealthImpl : public Health { |
| 216 | public: |
| 217 | HealthImpl(std::unique_ptr<healthd_config>&& config) |
| 218 | : Health(std::move(config)) {} |
| 219 | |
| 220 | // A subclass can override this if these information should be retrieved |
| 221 | // differently. |
| 222 | // Return<void> getChargeCounter(getChargeCounter_cb _hidl_cb) override; |
| 223 | // Return<void> getCurrentNow(getCurrentNow_cb _hidl_cb) override; |
| 224 | // Return<void> getCurrentAverage(getCurrentAverage_cb _hidl_cb) override; |
| 225 | // Return<void> getCapacity(getCapacity_cb _hidl_cb) override; |
| 226 | // Return<void> getEnergyCounter(getEnergyCounter_cb _hidl_cb) override; |
| 227 | // Return<void> getChargeStatus(getChargeStatus_cb _hidl_cb) override; |
| 228 | // Return<void> getStorageInfo(getStorageInfo_cb _hidl_cb) override; |
| 229 | // Return<void> getDiskStats(getDiskStats_cb _hidl_cb) override; |
| 230 | // Return<void> getHealthInfo(getHealthInfo_cb _hidl_cb) override; |
| 231 | |
| 232 | // Functions introduced in Health HAL 2.1. |
| 233 | // Return<void> getHealthConfig(getHealthConfig_cb _hidl_cb) override; |
| 234 | // Return<void> getHealthInfo_2_1(getHealthInfo_2_1_cb _hidl_cb) override; |
| 235 | // Return<void> shouldKeepScreenOn(shouldKeepScreenOn_cb _hidl_cb) override; |
| 236 | |
| 237 | protected: |
| 238 | // A subclass can override this to modify any health info object before |
| 239 | // returning to clients. This is similar to healthd_board_battery_update(). |
| 240 | // By default, it does nothing. |
| 241 | // void UpdateHealthInfo(HealthInfo* health_info) override; |
| 242 | }; |
| 243 | |
| 244 | } // namespace implementation |
| 245 | } // namespace V2_1 |
| 246 | } // namespace health |
| 247 | } // namespace hardware |
| 248 | } // namespace android |
| 249 | |
| 250 | extern "C" IHealth* HIDL_FETCH_IHealth(const char* instance) { |
| 251 | using ::android::hardware::health::V2_1::implementation::HealthImpl; |
| 252 | if (instance != "default"sv) { |
| 253 | return nullptr; |
| 254 | } |
| 255 | auto config = std::make_unique<healthd_config>(); |
| 256 | InitHealthdConfig(config.get()); |
| 257 | |
| 258 | // healthd_board_init(config.get()); |
| 259 | |
| 260 | return new HealthImpl(std::move(config)); |
| 261 | } |
| 262 | ``` |