blob: 8174bc8ea49fb22d1e0cc2b32f374694b6ef4d47 [file] [log] [blame]
Yifan Hong830cdb12021-01-11 20:47:23 -08001/*
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
29using std::string_literals::operator""s;
30
31namespace aidl::android::hardware::health {
32
33namespace {
34// Wrap LinkedCallback::OnCallbackDied() into a void(void*).
35void OnCallbackDiedWrapped(void* cookie) {
36 LinkedCallback* linked = reinterpret_cast<LinkedCallback*>(cookie);
37 linked->OnCallbackDied();
38}
39} // namespace
40
41/*
42// If you need to call healthd_board_init, construct the Health instance with
43// the healthd_config after calling healthd_board_init:
44class MyHealth : public Health {
45 protected:
46 MyHealth() : Health(CreateConfig()) {}
47 private:
48 static std::unique_ptr<healthd_config> CreateConfig() {
49 auto config = std::make_unique<healthd_config>();
50 ::android::hardware::health::InitHealthdConfig(config.get());
51 healthd_board_init(config.get());
52 return std::move(config);
53 }
54};
55*/
56Health::Health(std::string_view instance_name, std::unique_ptr<struct healthd_config>&& config)
57 : instance_name_(instance_name),
58 healthd_config_(std::move(config)),
59 death_recipient_(AIBinder_DeathRecipient_new(&OnCallbackDiedWrapped)) {
60 battery_monitor_.init(healthd_config_.get());
61}
62
Yifan Hong2d418a22021-11-12 18:13:51 -080063Health::~Health() {}
64
David Anderson8b413eb2023-12-08 15:27:18 -080065static inline ndk::ScopedAStatus TranslateStatus(::android::status_t err) {
66 switch (err) {
67 case ::android::OK:
68 return ndk::ScopedAStatus::ok();
69 case ::android::NAME_NOT_FOUND:
70 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
71 default:
72 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
73 IHealth::STATUS_UNKNOWN, ::android::statusToString(err).c_str());
74 }
75}
76
Yifan Hong830cdb12021-01-11 20:47:23 -080077//
78// Getters.
79//
80
81template <typename T>
82static ndk::ScopedAStatus GetProperty(::android::BatteryMonitor* monitor, int id, T defaultValue,
83 T* out) {
84 *out = defaultValue;
85 struct ::android::BatteryProperty prop;
86 ::android::status_t err = monitor->getProperty(static_cast<int>(id), &prop);
87 if (err == ::android::OK) {
88 *out = static_cast<T>(prop.valueInt64);
89 } else {
90 LOG(DEBUG) << "getProperty(" << id << ")"
91 << " fails: (" << err << ") " << ::android::statusToString(err);
92 }
David Anderson8b413eb2023-12-08 15:27:18 -080093 return TranslateStatus(err);
Yifan Hong830cdb12021-01-11 20:47:23 -080094}
95
96ndk::ScopedAStatus Health::getChargeCounterUah(int32_t* out) {
97 return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CHARGE_COUNTER, 0, out);
98}
99
100ndk::ScopedAStatus Health::getCurrentNowMicroamps(int32_t* out) {
101 return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_NOW, 0, out);
102}
103
104ndk::ScopedAStatus Health::getCurrentAverageMicroamps(int32_t* out) {
105 return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_AVG, 0, out);
106}
107
108ndk::ScopedAStatus Health::getCapacity(int32_t* out) {
109 return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CAPACITY, 0, out);
110}
111
112ndk::ScopedAStatus Health::getEnergyCounterNwh(int64_t* out) {
113 return GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_ENERGY_COUNTER, 0, out);
114}
115
116ndk::ScopedAStatus Health::getChargeStatus(BatteryStatus* out) {
117 return GetProperty(&battery_monitor_, ::android::BATTERY_PROP_BATTERY_STATUS,
118 BatteryStatus::UNKNOWN, out);
119}
120
Jack Wu33561612022-11-24 12:10:55 +0800121ndk::ScopedAStatus Health::setChargingPolicy(BatteryChargingPolicy in_value) {
122 ::android::status_t err = battery_monitor_.setChargingPolicy(static_cast<int>(in_value));
123
124 switch (err) {
125 case ::android::OK:
126 return ndk::ScopedAStatus::ok();
127 case ::android::NAME_NOT_FOUND:
128 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
129 case ::android::BAD_VALUE:
130 return ndk::ScopedAStatus::fromStatus(::android::INVALID_OPERATION);
131 default:
132 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
133 IHealth::STATUS_UNKNOWN, ::android::statusToString(err).c_str());
134 }
135}
136
137ndk::ScopedAStatus Health::getChargingPolicy(BatteryChargingPolicy* out) {
138 return GetProperty(&battery_monitor_, ::android::BATTERY_PROP_CHARGING_POLICY,
139 BatteryChargingPolicy::DEFAULT, out);
140}
141
142ndk::ScopedAStatus Health::getBatteryHealthData(BatteryHealthData* out) {
143 if (auto res =
144 GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_MANUFACTURING_DATE,
145 0, &out->batteryManufacturingDateSeconds);
146 !res.isOk()) {
147 LOG(WARNING) << "Cannot get Manufacturing_date: " << res.getDescription();
148 }
149 if (auto res = GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_FIRST_USAGE_DATE,
150 0, &out->batteryFirstUsageSeconds);
151 !res.isOk()) {
152 LOG(WARNING) << "Cannot get First_usage_date: " << res.getDescription();
153 }
Jack Wucbbf24f2023-02-18 12:34:19 +0800154 if (auto res = GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_STATE_OF_HEALTH,
155 0, &out->batteryStateOfHealth);
156 !res.isOk()) {
157 LOG(WARNING) << "Cannot get Battery_state_of_health: " << res.getDescription();
158 }
David Anderson8b413eb2023-12-08 15:27:18 -0800159 if (auto res = battery_monitor_.getSerialNumber(&out->batterySerialNumber);
160 res != ::android::OK) {
161 LOG(WARNING) << "Cannot get Battery_serial_number: "
162 << TranslateStatus(res).getDescription();
163 }
164
165 int64_t part_status = static_cast<int64_t>(BatteryPartStatus::UNSUPPORTED);
166 if (auto res = GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_PART_STATUS,
167 static_cast<int64_t>(BatteryPartStatus::UNSUPPORTED),
168 &part_status);
169 !res.isOk()) {
170 LOG(WARNING) << "Cannot get Battery_part_status: " << res.getDescription();
171 }
172 out->batteryPartStatus = static_cast<BatteryPartStatus>(part_status);
173
Jack Wu33561612022-11-24 12:10:55 +0800174 return ndk::ScopedAStatus::ok();
175}
176
Yifan Hong830cdb12021-01-11 20:47:23 -0800177ndk::ScopedAStatus Health::getDiskStats(std::vector<DiskStats>*) {
178 // This implementation does not support DiskStats. An implementation may extend this
179 // class and override this function to support disk stats.
180 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
181}
182
183ndk::ScopedAStatus Health::getStorageInfo(std::vector<StorageInfo>*) {
184 // This implementation does not support StorageInfo. An implementation may extend this
185 // class and override this function to support storage info.
186 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
187}
188
189ndk::ScopedAStatus Health::getHealthInfo(HealthInfo* out) {
190 battery_monitor_.updateValues();
191
Yifan Hongbc84a792022-03-01 12:24:55 -0800192 *out = battery_monitor_.getHealthInfo();
Yifan Hong830cdb12021-01-11 20:47:23 -0800193
194 // Fill in storage infos; these aren't retrieved by BatteryMonitor.
195 if (auto res = getStorageInfo(&out->storageInfos); !res.isOk()) {
196 if (res.getServiceSpecificError() == 0 &&
197 res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
198 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
199 IHealth::STATUS_UNKNOWN,
200 ("getStorageInfo fails: " + res.getDescription()).c_str());
201 }
202 LOG(DEBUG) << "getHealthInfo: getStorageInfo fails with service-specific error, clearing: "
203 << res.getDescription();
204 out->storageInfos = {};
205 }
206 if (auto res = getDiskStats(&out->diskStats); !res.isOk()) {
207 if (res.getServiceSpecificError() == 0 &&
208 res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
209 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
210 IHealth::STATUS_UNKNOWN,
211 ("getDiskStats fails: " + res.getDescription()).c_str());
212 }
213 LOG(DEBUG) << "getHealthInfo: getDiskStats fails with service-specific error, clearing: "
214 << res.getDescription();
215 out->diskStats = {};
216 }
217
218 // A subclass may want to update health info struct before returning it.
219 UpdateHealthInfo(out);
220
221 return ndk::ScopedAStatus::ok();
222}
223
224binder_status_t Health::dump(int fd, const char**, uint32_t) {
225 battery_monitor_.dumpState(fd);
226
227 ::android::base::WriteStringToFd("\ngetHealthInfo -> ", fd);
228 HealthInfo health_info;
229 auto res = getHealthInfo(&health_info);
230 if (res.isOk()) {
231 ::android::base::WriteStringToFd(health_info.toString(), fd);
232 } else {
233 ::android::base::WriteStringToFd(res.getDescription(), fd);
234 }
Kevin Jeon03317722023-04-13 12:14:11 -0400235 ::android::base::WriteStringToFd("\n", fd);
Yifan Hong830cdb12021-01-11 20:47:23 -0800236
237 fsync(fd);
238 return STATUS_OK;
239}
240
241std::optional<bool> Health::ShouldKeepScreenOn() {
242 if (!healthd_config_->screen_on) {
243 return std::nullopt;
244 }
245
246 HealthInfo health_info;
247 auto res = getHealthInfo(&health_info);
248 if (!res.isOk()) {
249 return std::nullopt;
250 }
251
252 ::android::BatteryProperties props = {};
253 convert(health_info, &props);
254 return healthd_config_->screen_on(&props);
255}
256
Yifan Hong830cdb12021-01-11 20:47:23 -0800257//
258// Subclass helpers / overrides
259//
260
261void Health::UpdateHealthInfo(HealthInfo* /* health_info */) {
262 /*
263 // Sample code for a subclass to implement this:
264 // If you need to modify values (e.g. batteryChargeTimeToFullNowSeconds), do it here.
265 health_info->batteryChargeTimeToFullNowSeconds = calculate_charge_time_seconds();
266
267 // If you need to call healthd_board_battery_update, modify its signature
268 // and implementation to operate on HealthInfo directly, then call:
269 healthd_board_battery_update(health_info);
270 */
271}
272
273//
274// Methods that handle callbacks.
275//
276
277ndk::ScopedAStatus Health::registerCallback(const std::shared_ptr<IHealthInfoCallback>& callback) {
278 if (callback == nullptr) {
279 // For now, this shouldn't happen because argument is not nullable.
280 return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
281 }
282
283 {
284 std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
Yifan Hong70197262023-07-06 17:08:41 -0700285 auto linked_callback_result = LinkedCallback::Make(ref<Health>(), callback);
286 if (!linked_callback_result.ok()) {
287 return ndk::ScopedAStatus::fromStatus(-linked_callback_result.error().code());
288 }
289 callbacks_.emplace_back(std::move(*linked_callback_result));
Yifan Hong830cdb12021-01-11 20:47:23 -0800290 // unlock
291 }
292
293 HealthInfo health_info;
294 if (auto res = getHealthInfo(&health_info); !res.isOk()) {
295 LOG(WARNING) << "Cannot call getHealthInfo: " << res.getDescription();
296 // No health info to send, so return early.
297 return ndk::ScopedAStatus::ok();
298 }
299
Hang Lucef01a32023-08-30 18:27:25 +0800300 auto res = callback->healthInfoChanged(health_info);
301 if (!res.isOk()) {
302 LOG(DEBUG) << "Cannot call healthInfoChanged:" << res.getDescription()
303 << ". Do nothing here if callback is dead as it will be cleaned up later.";
Yifan Hong830cdb12021-01-11 20:47:23 -0800304 }
305 return ndk::ScopedAStatus::ok();
306}
307
308ndk::ScopedAStatus Health::unregisterCallback(
309 const std::shared_ptr<IHealthInfoCallback>& callback) {
310 if (callback == nullptr) {
311 // For now, this shouldn't happen because argument is not nullable.
312 return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
313 }
314
315 std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
316
317 auto matches = [callback](const auto& linked) {
Yifan Hong6839f672021-10-28 22:24:35 -0700318 return linked->callback()->asBinder() == callback->asBinder(); // compares binder object
Yifan Hong830cdb12021-01-11 20:47:23 -0800319 };
320 auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), matches);
321 bool removed = (it != callbacks_.end());
322 callbacks_.erase(it, callbacks_.end()); // calls unlinkToDeath on deleted callbacks.
323 return removed ? ndk::ScopedAStatus::ok()
324 : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
325}
326
327// A combination of the HIDL version
328// android::hardware::health::V2_1::implementation::Health::update() and
329// android::hardware::health::V2_1::implementation::BinderHealth::update()
330ndk::ScopedAStatus Health::update() {
331 HealthInfo health_info;
332 if (auto res = getHealthInfo(&health_info); !res.isOk()) {
333 LOG(DEBUG) << "Cannot call getHealthInfo for update(): " << res.getDescription();
334 // Propagate service specific errors. If there's none, report unknown error.
335 if (res.getServiceSpecificError() != 0 ||
336 res.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
337 return res;
338 }
339 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
340 IHealth::STATUS_UNKNOWN, res.getDescription().c_str());
341 }
342 battery_monitor_.logValues();
343 OnHealthInfoChanged(health_info);
344 return ndk::ScopedAStatus::ok();
345}
346
347void Health::OnHealthInfoChanged(const HealthInfo& health_info) {
348 // Notify all callbacks
349 std::unique_lock<decltype(callbacks_lock_)> lock(callbacks_lock_);
Hang Lucef01a32023-08-30 18:27:25 +0800350 for (const auto& linked : callbacks_) {
Yifan Hong830cdb12021-01-11 20:47:23 -0800351 auto res = linked->callback()->healthInfoChanged(health_info);
Hang Lucef01a32023-08-30 18:27:25 +0800352 if (!res.isOk()) {
353 LOG(DEBUG) << "Cannot call healthInfoChanged:" << res.getDescription()
354 << ". Do nothing here if callback is dead as it will be cleaned up later.";
355 }
356 }
Yifan Hong830cdb12021-01-11 20:47:23 -0800357 lock.unlock();
358
359 // Let HalHealthLoop::OnHealthInfoChanged() adjusts uevent / wakealarm periods
360}
361
362void Health::BinderEvent(uint32_t /*epevents*/) {
363 if (binder_fd_ >= 0) {
364 ABinderProcess_handlePolledCommands();
365 }
366}
367
368void Health::OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) {
369 LOG(INFO) << instance_name_ << " instance initializing with healthd_config...";
370
371 // Similar to HIDL's android::hardware::health::V2_1::implementation::HalHealthLoop::Init,
372 // copy configuration parameters to |config| for HealthLoop (e.g. uevent / wake alarm periods)
373 *config = *healthd_config_.get();
374
375 binder_status_t status = ABinderProcess_setupPolling(&binder_fd_);
376
377 if (status == ::STATUS_OK && binder_fd_ >= 0) {
378 std::shared_ptr<Health> thiz = ref<Health>();
379 auto binder_event = [thiz](auto*, uint32_t epevents) { thiz->BinderEvent(epevents); };
380 if (hal_health_loop->RegisterEvent(binder_fd_, binder_event, EVENT_NO_WAKEUP_FD) != 0) {
381 PLOG(ERROR) << instance_name_ << " instance: Register for binder events failed";
382 }
383 }
384
385 std::string health_name = IHealth::descriptor + "/"s + instance_name_;
386 CHECK_EQ(STATUS_OK, AServiceManager_addService(this->asBinder().get(), health_name.c_str()))
387 << instance_name_ << ": Failed to register HAL";
388
389 LOG(INFO) << instance_name_ << ": Hal init done";
390}
391
392// Unlike hwbinder, for binder, there's no need to explicitly call flushCommands()
393// in PrepareToWait(). See b/139697085.
394
395} // namespace aidl::android::hardware::health