1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "health-impl/Health.h"
18
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <android/binder_manager.h>
22 #include <android/binder_process.h>
23 #include <android/hardware/health/translate-ndk.h>
24 #include <health/utils.h>
25
26 #include "LinkedCallback.h"
27 #include "health-convert.h"
28
29 using std::string_literals::operator""s;
30
31 namespace aidl::android::hardware::health {
32
33 namespace {
34 // Wrap LinkedCallback::OnCallbackDied() into a void(void*).
OnCallbackDiedWrapped(void * cookie)35 void OnCallbackDiedWrapped(void* cookie) {
36 LinkedCallback* linked = reinterpret_cast<LinkedCallback*>(cookie);
37 linked->OnCallbackDied();
38 }
39 // Delete the owned cookie.
onCallbackUnlinked(void * cookie)40 void onCallbackUnlinked(void* cookie) {
41 LinkedCallback* linked = reinterpret_cast<LinkedCallback*>(cookie);
42 delete linked;
43 }
44 } // namespace
45
46 /*
47 // If you need to call healthd_board_init, construct the Health instance with
48 // the healthd_config after calling healthd_board_init:
49 class MyHealth : public Health {
50 protected:
51 MyHealth() : Health(CreateConfig()) {}
52 private:
53 static std::unique_ptr<healthd_config> CreateConfig() {
54 auto config = std::make_unique<healthd_config>();
55 ::android::hardware::health::InitHealthdConfig(config.get());
56 healthd_board_init(config.get());
57 return std::move(config);
58 }
59 };
60 */
Health(std::string_view instance_name,std::unique_ptr<struct healthd_config> && config)61 Health::Health(std::string_view instance_name, std::unique_ptr<struct healthd_config>&& config)
62 : instance_name_(instance_name),
63 healthd_config_(std::move(config)),
64 death_recipient_(AIBinder_DeathRecipient_new(&OnCallbackDiedWrapped)) {
65 AIBinder_DeathRecipient_setOnUnlinked(death_recipient_.get(), onCallbackUnlinked);
66 battery_monitor_.init(healthd_config_.get());
67 }
68
~Health()69 Health::~Health() {}
70
TranslateStatus(::android::status_t err)71 static inline ndk::ScopedAStatus TranslateStatus(::android::status_t err) {
72 switch (err) {
73 case ::android::OK:
74 return ndk::ScopedAStatus::ok();
75 case ::android::NAME_NOT_FOUND:
76 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
77 default:
78 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
79 IHealth::STATUS_UNKNOWN, ::android::statusToString(err).c_str());
80 }
81 }
82
83 //
84 // Getters.
85 //
86
87 template <typename T>
GetProperty(::android::BatteryMonitor * monitor,int id,T defaultValue,T * out)88 static ndk::ScopedAStatus GetProperty(::android::BatteryMonitor* monitor, int id, T defaultValue,
89 T* out) {
90 *out = defaultValue;
91 struct ::android::BatteryProperty prop;
92 ::android::status_t err = monitor->getProperty(static_cast<int>(id), &prop);
93 if (err == ::android::OK) {
94 *out = static_cast<T>(prop.valueInt64);
95 } else {
96 LOG(DEBUG) << "getProperty(" << id << ")"
97 << " fails: (" << err << ") " << ::android::statusToString(err);
98 }
99 return TranslateStatus(err);
100 }
101
getChargeCounterUah(int32_t * out)102 ndk::ScopedAStatus Health::getChargeCounterUah(int32_t* out) {
103 return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CHARGE_COUNTER, 0, out);
104 }
105
getCurrentNowMicroamps(int32_t * out)106 ndk::ScopedAStatus Health::getCurrentNowMicroamps(int32_t* out) {
107 return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_NOW, 0, out);
108 }
109
getCurrentAverageMicroamps(int32_t * out)110 ndk::ScopedAStatus Health::getCurrentAverageMicroamps(int32_t* out) {
111 return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_AVG, 0, out);
112 }
113
getCapacity(int32_t * out)114 ndk::ScopedAStatus Health::getCapacity(int32_t* out) {
115 return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CAPACITY, 0, out);
116 }
117
getEnergyCounterNwh(int64_t * out)118 ndk::ScopedAStatus Health::getEnergyCounterNwh(int64_t* out) {
119 return GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_ENERGY_COUNTER, 0, out);
120 }
121
getChargeStatus(BatteryStatus * out)122 ndk::ScopedAStatus Health::getChargeStatus(BatteryStatus* out) {
123 return GetProperty(&battery_monitor_, ::android::BATTERY_PROP_BATTERY_STATUS,
124 BatteryStatus::UNKNOWN, out);
125 }
126
setChargingPolicy(BatteryChargingPolicy in_value)127 ndk::ScopedAStatus Health::setChargingPolicy(BatteryChargingPolicy in_value) {
128 ::android::status_t err = battery_monitor_.setChargingPolicy(static_cast<int>(in_value));
129
130 switch (err) {
131 case ::android::OK:
132 return ndk::ScopedAStatus::ok();
133 case ::android::NAME_NOT_FOUND:
134 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
135 case ::android::BAD_VALUE:
136 return ndk::ScopedAStatus::fromStatus(::android::INVALID_OPERATION);
137 default:
138 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
139 IHealth::STATUS_UNKNOWN, ::android::statusToString(err).c_str());
140 }
141 }
142
getChargingPolicy(BatteryChargingPolicy * out)143 ndk::ScopedAStatus Health::getChargingPolicy(BatteryChargingPolicy* out) {
144 return GetProperty(&battery_monitor_, ::android::BATTERY_PROP_CHARGING_POLICY,
145 BatteryChargingPolicy::DEFAULT, out);
146 }
147
getBatteryHealthData(BatteryHealthData * out)148 ndk::ScopedAStatus Health::getBatteryHealthData(BatteryHealthData* out) {
149 if (auto res =
150 GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_MANUFACTURING_DATE,
151 0, &out->batteryManufacturingDateSeconds);
152 !res.isOk()) {
153 LOG(WARNING) << "Cannot get Manufacturing_date: " << res.getDescription();
154 }
155 if (auto res = GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_FIRST_USAGE_DATE,
156 0, &out->batteryFirstUsageSeconds);
157 !res.isOk()) {
158 LOG(WARNING) << "Cannot get First_usage_date: " << res.getDescription();
159 }
160 if (auto res = GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_STATE_OF_HEALTH,
161 0, &out->batteryStateOfHealth);
162 !res.isOk()) {
163 LOG(WARNING) << "Cannot get Battery_state_of_health: " << res.getDescription();
164 }
165 if (auto res = battery_monitor_.getSerialNumber(&out->batterySerialNumber);
166 res != ::android::OK) {
167 LOG(WARNING) << "Cannot get Battery_serial_number: "
168 << TranslateStatus(res).getDescription();
169 }
170
171 int64_t part_status = static_cast<int64_t>(BatteryPartStatus::UNSUPPORTED);
172 if (auto res = GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_PART_STATUS,
173 static_cast<int64_t>(BatteryPartStatus::UNSUPPORTED),
174 &part_status);
175 !res.isOk()) {
176 LOG(WARNING) << "Cannot get Battery_part_status: " << res.getDescription();
177 }
178 out->batteryPartStatus = static_cast<BatteryPartStatus>(part_status);
179
180 return ndk::ScopedAStatus::ok();
181 }
182
getDiskStats(std::vector<DiskStats> *)183 ndk::ScopedAStatus Health::getDiskStats(std::vector<DiskStats>*) {
184 // This implementation does not support DiskStats. An implementation may extend this
185 // class and override this function to support disk stats.
186 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
187 }
188
getStorageInfo(std::vector<StorageInfo> *)189 ndk::ScopedAStatus Health::getStorageInfo(std::vector<StorageInfo>*) {
190 // This implementation does not support StorageInfo. An implementation may extend this
191 // class and override this function to support storage info.
192 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
193 }
194
getHealthInfo(HealthInfo * out)195 ndk::ScopedAStatus Health::getHealthInfo(HealthInfo* out) {
196 battery_monitor_.updateValues();
197
198 *out = battery_monitor_.getHealthInfo();
199
200 // Fill in storage infos; these aren't retrieved by BatteryMonitor.
201 if (auto res = getStorageInfo(&out->storageInfos); !res.isOk()) {
202 if (res.getServiceSpecificError() == 0 &&
203 res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
204 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
205 IHealth::STATUS_UNKNOWN,
206 ("getStorageInfo fails: " + res.getDescription()).c_str());
207 }
208 LOG(DEBUG) << "getHealthInfo: getStorageInfo fails with service-specific error, clearing: "
209 << res.getDescription();
210 out->storageInfos = {};
211 }
212 if (auto res = getDiskStats(&out->diskStats); !res.isOk()) {
213 if (res.getServiceSpecificError() == 0 &&
214 res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
215 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
216 IHealth::STATUS_UNKNOWN,
217 ("getDiskStats fails: " + res.getDescription()).c_str());
218 }
219 LOG(DEBUG) << "getHealthInfo: getDiskStats fails with service-specific error, clearing: "
220 << res.getDescription();
221 out->diskStats = {};
222 }
223
224 // A subclass may want to update health info struct before returning it.
225 UpdateHealthInfo(out);
226
227 return ndk::ScopedAStatus::ok();
228 }
229
dump(int fd,const char **,uint32_t)230 binder_status_t Health::dump(int fd, const char**, uint32_t) {
231 battery_monitor_.dumpState(fd);
232
233 ::android::base::WriteStringToFd("\ngetHealthInfo -> ", fd);
234 HealthInfo health_info;
235 auto res = getHealthInfo(&health_info);
236 if (res.isOk()) {
237 ::android::base::WriteStringToFd(health_info.toString(), fd);
238 } else {
239 ::android::base::WriteStringToFd(res.getDescription(), fd);
240 }
241 ::android::base::WriteStringToFd("\n", fd);
242
243 fsync(fd);
244 return STATUS_OK;
245 }
246
ShouldKeepScreenOn()247 std::optional<bool> Health::ShouldKeepScreenOn() {
248 if (!healthd_config_->screen_on) {
249 return std::nullopt;
250 }
251
252 HealthInfo health_info;
253 auto res = getHealthInfo(&health_info);
254 if (!res.isOk()) {
255 return std::nullopt;
256 }
257
258 ::android::BatteryProperties props = {};
259 convert(health_info, &props);
260 return healthd_config_->screen_on(&props);
261 }
262
263 //
264 // Subclass helpers / overrides
265 //
266
UpdateHealthInfo(HealthInfo *)267 void Health::UpdateHealthInfo(HealthInfo* /* health_info */) {
268 /*
269 // Sample code for a subclass to implement this:
270 // If you need to modify values (e.g. batteryChargeTimeToFullNowSeconds), do it here.
271 health_info->batteryChargeTimeToFullNowSeconds = calculate_charge_time_seconds();
272
273 // If you need to call healthd_board_battery_update, modify its signature
274 // and implementation to operate on HealthInfo directly, then call:
275 healthd_board_battery_update(health_info);
276 */
277 }
278
279 //
280 // Methods that handle callbacks.
281 //
282
registerCallback(const std::shared_ptr<IHealthInfoCallback> & callback)283 ndk::ScopedAStatus Health::registerCallback(const std::shared_ptr<IHealthInfoCallback>& callback) {
284 if (callback == nullptr) {
285 // For now, this shouldn't happen because argument is not nullable.
286 return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
287 }
288
289 {
290 std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
291 auto linked_callback_result = LinkedCallback::Make(ref<Health>(), callback);
292 if (!linked_callback_result.ok()) {
293 return ndk::ScopedAStatus::fromStatus(-linked_callback_result.error().code());
294 }
295 callbacks_[*linked_callback_result] = callback;
296 // unlock
297 }
298
299 HealthInfo health_info;
300 if (auto res = getHealthInfo(&health_info); !res.isOk()) {
301 LOG(WARNING) << "Cannot call getHealthInfo: " << res.getDescription();
302 // No health info to send, so return early.
303 return ndk::ScopedAStatus::ok();
304 }
305
306 auto res = callback->healthInfoChanged(health_info);
307 if (!res.isOk()) {
308 LOG(DEBUG) << "Cannot call healthInfoChanged:" << res.getDescription()
309 << ". Do nothing here if callback is dead as it will be cleaned up later.";
310 }
311 return ndk::ScopedAStatus::ok();
312 }
313
unregisterCallback(const std::shared_ptr<IHealthInfoCallback> & callback)314 ndk::ScopedAStatus Health::unregisterCallback(
315 const std::shared_ptr<IHealthInfoCallback>& callback) {
316 if (callback == nullptr) {
317 // For now, this shouldn't happen because argument is not nullable.
318 return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
319 }
320
321 std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
322
323 auto matches = [callback](const auto& cb) {
324 return cb->asBinder() == callback->asBinder(); // compares binder object
325 };
326 bool removed = false;
327 for (auto it = callbacks_.begin(); it != callbacks_.end();) {
328 if (it->second->asBinder() == callback->asBinder()) {
329 auto status = AIBinder_unlinkToDeath(callback->asBinder().get(), death_recipient_.get(),
330 reinterpret_cast<void*>(it->first));
331 if (status != STATUS_OK && status != STATUS_DEAD_OBJECT) {
332 LOG(WARNING) << __func__
333 << "Cannot unlink to death: " << ::android::statusToString(status);
334 }
335 it = callbacks_.erase(it);
336 removed = true;
337 } else {
338 it++;
339 }
340 }
341 return removed ? ndk::ScopedAStatus::ok()
342 : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
343 }
344
345 // A combination of the HIDL version
346 // android::hardware::health::V2_1::implementation::Health::update() and
347 // android::hardware::health::V2_1::implementation::BinderHealth::update()
update()348 ndk::ScopedAStatus Health::update() {
349 HealthInfo health_info;
350 if (auto res = getHealthInfo(&health_info); !res.isOk()) {
351 LOG(DEBUG) << "Cannot call getHealthInfo for update(): " << res.getDescription();
352 // Propagate service specific errors. If there's none, report unknown error.
353 if (res.getServiceSpecificError() != 0 ||
354 res.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
355 return res;
356 }
357 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
358 IHealth::STATUS_UNKNOWN, res.getDescription().c_str());
359 }
360 battery_monitor_.logValues();
361 OnHealthInfoChanged(health_info);
362 return ndk::ScopedAStatus::ok();
363 }
364
OnHealthInfoChanged(const HealthInfo & health_info)365 void Health::OnHealthInfoChanged(const HealthInfo& health_info) {
366 // Notify all callbacks
367 std::unique_lock<decltype(callbacks_lock_)> lock(callbacks_lock_);
368 for (const auto& [_, callback] : callbacks_) {
369 auto res = callback->healthInfoChanged(health_info);
370 if (!res.isOk()) {
371 LOG(DEBUG) << "Cannot call healthInfoChanged:" << res.getDescription()
372 << ". Do nothing here if callback is dead as it will be cleaned up later.";
373 }
374 }
375 lock.unlock();
376
377 // Let HalHealthLoop::OnHealthInfoChanged() adjusts uevent / wakealarm periods
378 }
379
BinderEvent(uint32_t)380 void Health::BinderEvent(uint32_t /*epevents*/) {
381 if (binder_fd_ >= 0) {
382 ABinderProcess_handlePolledCommands();
383 }
384 }
385
OnInit(HalHealthLoop * hal_health_loop,struct healthd_config * config)386 void Health::OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) {
387 LOG(INFO) << instance_name_ << " instance initializing with healthd_config...";
388
389 // Similar to HIDL's android::hardware::health::V2_1::implementation::HalHealthLoop::Init,
390 // copy configuration parameters to |config| for HealthLoop (e.g. uevent / wake alarm periods)
391 *config = *healthd_config_.get();
392
393 binder_status_t status = ABinderProcess_setupPolling(&binder_fd_);
394
395 if (status == ::STATUS_OK && binder_fd_ >= 0) {
396 std::shared_ptr<Health> thiz = ref<Health>();
397 auto binder_event = [thiz](auto*, uint32_t epevents) { thiz->BinderEvent(epevents); };
398 if (hal_health_loop->RegisterEvent(binder_fd_, binder_event, EVENT_NO_WAKEUP_FD) != 0) {
399 PLOG(ERROR) << instance_name_ << " instance: Register for binder events failed";
400 }
401 }
402
403 std::string health_name = IHealth::descriptor + "/"s + instance_name_;
404 CHECK_EQ(STATUS_OK, AServiceManager_addService(this->asBinder().get(), health_name.c_str()))
405 << instance_name_ << ": Failed to register HAL";
406
407 LOG(INFO) << instance_name_ << ": Hal init done";
408 }
409
410 // Unlike hwbinder, for binder, there's no need to explicitly call flushCommands()
411 // in PrepareToWait(). See b/139697085.
412
413 } // namespace aidl::android::hardware::health
414