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 } // 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:
44 class 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 */
Health(std::string_view instance_name,std::unique_ptr<struct healthd_config> && config)56 Health::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
~Health()63 Health::~Health() {}
64
65 //
66 // Getters.
67 //
68
69 template <typename T>
GetProperty(::android::BatteryMonitor * monitor,int id,T defaultValue,T * out)70 static ndk::ScopedAStatus GetProperty(::android::BatteryMonitor* monitor, int id, T defaultValue,
71 T* out) {
72 *out = defaultValue;
73 struct ::android::BatteryProperty prop;
74 ::android::status_t err = monitor->getProperty(static_cast<int>(id), &prop);
75 if (err == ::android::OK) {
76 *out = static_cast<T>(prop.valueInt64);
77 } else {
78 LOG(DEBUG) << "getProperty(" << id << ")"
79 << " fails: (" << err << ") " << ::android::statusToString(err);
80 }
81
82 switch (err) {
83 case ::android::OK:
84 return ndk::ScopedAStatus::ok();
85 case ::android::NAME_NOT_FOUND:
86 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
87 default:
88 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
89 IHealth::STATUS_UNKNOWN, ::android::statusToString(err).c_str());
90 }
91 }
92
getChargeCounterUah(int32_t * out)93 ndk::ScopedAStatus Health::getChargeCounterUah(int32_t* out) {
94 return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CHARGE_COUNTER, 0, out);
95 }
96
getCurrentNowMicroamps(int32_t * out)97 ndk::ScopedAStatus Health::getCurrentNowMicroamps(int32_t* out) {
98 return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_NOW, 0, out);
99 }
100
getCurrentAverageMicroamps(int32_t * out)101 ndk::ScopedAStatus Health::getCurrentAverageMicroamps(int32_t* out) {
102 return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_AVG, 0, out);
103 }
104
getCapacity(int32_t * out)105 ndk::ScopedAStatus Health::getCapacity(int32_t* out) {
106 return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CAPACITY, 0, out);
107 }
108
getEnergyCounterNwh(int64_t * out)109 ndk::ScopedAStatus Health::getEnergyCounterNwh(int64_t* out) {
110 return GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_ENERGY_COUNTER, 0, out);
111 }
112
getChargeStatus(BatteryStatus * out)113 ndk::ScopedAStatus Health::getChargeStatus(BatteryStatus* out) {
114 return GetProperty(&battery_monitor_, ::android::BATTERY_PROP_BATTERY_STATUS,
115 BatteryStatus::UNKNOWN, out);
116 }
117
setChargingPolicy(BatteryChargingPolicy in_value)118 ndk::ScopedAStatus Health::setChargingPolicy(BatteryChargingPolicy in_value) {
119 ::android::status_t err = battery_monitor_.setChargingPolicy(static_cast<int>(in_value));
120
121 switch (err) {
122 case ::android::OK:
123 return ndk::ScopedAStatus::ok();
124 case ::android::NAME_NOT_FOUND:
125 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
126 case ::android::BAD_VALUE:
127 return ndk::ScopedAStatus::fromStatus(::android::INVALID_OPERATION);
128 default:
129 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
130 IHealth::STATUS_UNKNOWN, ::android::statusToString(err).c_str());
131 }
132 }
133
getChargingPolicy(BatteryChargingPolicy * out)134 ndk::ScopedAStatus Health::getChargingPolicy(BatteryChargingPolicy* out) {
135 return GetProperty(&battery_monitor_, ::android::BATTERY_PROP_CHARGING_POLICY,
136 BatteryChargingPolicy::DEFAULT, out);
137 }
138
getBatteryHealthData(BatteryHealthData * out)139 ndk::ScopedAStatus Health::getBatteryHealthData(BatteryHealthData* out) {
140 if (auto res =
141 GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_MANUFACTURING_DATE,
142 0, &out->batteryManufacturingDateSeconds);
143 !res.isOk()) {
144 LOG(WARNING) << "Cannot get Manufacturing_date: " << res.getDescription();
145 }
146 if (auto res = GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_FIRST_USAGE_DATE,
147 0, &out->batteryFirstUsageSeconds);
148 !res.isOk()) {
149 LOG(WARNING) << "Cannot get First_usage_date: " << res.getDescription();
150 }
151 if (auto res = GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_STATE_OF_HEALTH,
152 0, &out->batteryStateOfHealth);
153 !res.isOk()) {
154 LOG(WARNING) << "Cannot get Battery_state_of_health: " << res.getDescription();
155 }
156 return ndk::ScopedAStatus::ok();
157 }
158
getDiskStats(std::vector<DiskStats> *)159 ndk::ScopedAStatus Health::getDiskStats(std::vector<DiskStats>*) {
160 // This implementation does not support DiskStats. An implementation may extend this
161 // class and override this function to support disk stats.
162 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
163 }
164
getStorageInfo(std::vector<StorageInfo> *)165 ndk::ScopedAStatus Health::getStorageInfo(std::vector<StorageInfo>*) {
166 // This implementation does not support StorageInfo. An implementation may extend this
167 // class and override this function to support storage info.
168 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
169 }
170
getHealthInfo(HealthInfo * out)171 ndk::ScopedAStatus Health::getHealthInfo(HealthInfo* out) {
172 battery_monitor_.updateValues();
173
174 *out = battery_monitor_.getHealthInfo();
175
176 // Fill in storage infos; these aren't retrieved by BatteryMonitor.
177 if (auto res = getStorageInfo(&out->storageInfos); !res.isOk()) {
178 if (res.getServiceSpecificError() == 0 &&
179 res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
180 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
181 IHealth::STATUS_UNKNOWN,
182 ("getStorageInfo fails: " + res.getDescription()).c_str());
183 }
184 LOG(DEBUG) << "getHealthInfo: getStorageInfo fails with service-specific error, clearing: "
185 << res.getDescription();
186 out->storageInfos = {};
187 }
188 if (auto res = getDiskStats(&out->diskStats); !res.isOk()) {
189 if (res.getServiceSpecificError() == 0 &&
190 res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
191 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
192 IHealth::STATUS_UNKNOWN,
193 ("getDiskStats fails: " + res.getDescription()).c_str());
194 }
195 LOG(DEBUG) << "getHealthInfo: getDiskStats fails with service-specific error, clearing: "
196 << res.getDescription();
197 out->diskStats = {};
198 }
199
200 // A subclass may want to update health info struct before returning it.
201 UpdateHealthInfo(out);
202
203 return ndk::ScopedAStatus::ok();
204 }
205
dump(int fd,const char **,uint32_t)206 binder_status_t Health::dump(int fd, const char**, uint32_t) {
207 battery_monitor_.dumpState(fd);
208
209 ::android::base::WriteStringToFd("\ngetHealthInfo -> ", fd);
210 HealthInfo health_info;
211 auto res = getHealthInfo(&health_info);
212 if (res.isOk()) {
213 ::android::base::WriteStringToFd(health_info.toString(), fd);
214 } else {
215 ::android::base::WriteStringToFd(res.getDescription(), fd);
216 }
217
218 fsync(fd);
219 return STATUS_OK;
220 }
221
ShouldKeepScreenOn()222 std::optional<bool> Health::ShouldKeepScreenOn() {
223 if (!healthd_config_->screen_on) {
224 return std::nullopt;
225 }
226
227 HealthInfo health_info;
228 auto res = getHealthInfo(&health_info);
229 if (!res.isOk()) {
230 return std::nullopt;
231 }
232
233 ::android::BatteryProperties props = {};
234 convert(health_info, &props);
235 return healthd_config_->screen_on(&props);
236 }
237
238 namespace {
IsDeadObjectLogged(const ndk::ScopedAStatus & ret)239 bool IsDeadObjectLogged(const ndk::ScopedAStatus& ret) {
240 if (ret.isOk()) return false;
241 if (ret.getStatus() == ::STATUS_DEAD_OBJECT) return true;
242 LOG(ERROR) << "Cannot call healthInfoChanged on callback: " << ret.getDescription();
243 return false;
244 }
245 } // namespace
246
247 //
248 // Subclass helpers / overrides
249 //
250
UpdateHealthInfo(HealthInfo *)251 void Health::UpdateHealthInfo(HealthInfo* /* health_info */) {
252 /*
253 // Sample code for a subclass to implement this:
254 // If you need to modify values (e.g. batteryChargeTimeToFullNowSeconds), do it here.
255 health_info->batteryChargeTimeToFullNowSeconds = calculate_charge_time_seconds();
256
257 // If you need to call healthd_board_battery_update, modify its signature
258 // and implementation to operate on HealthInfo directly, then call:
259 healthd_board_battery_update(health_info);
260 */
261 }
262
263 //
264 // Methods that handle callbacks.
265 //
266
registerCallback(const std::shared_ptr<IHealthInfoCallback> & callback)267 ndk::ScopedAStatus Health::registerCallback(const std::shared_ptr<IHealthInfoCallback>& callback) {
268 if (callback == nullptr) {
269 // For now, this shouldn't happen because argument is not nullable.
270 return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
271 }
272
273 {
274 std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
275 callbacks_.emplace_back(LinkedCallback::Make(ref<Health>(), callback));
276 // unlock
277 }
278
279 HealthInfo health_info;
280 if (auto res = getHealthInfo(&health_info); !res.isOk()) {
281 LOG(WARNING) << "Cannot call getHealthInfo: " << res.getDescription();
282 // No health info to send, so return early.
283 return ndk::ScopedAStatus::ok();
284 }
285
286 if (auto res = callback->healthInfoChanged(health_info); IsDeadObjectLogged(res)) {
287 (void)unregisterCallback(callback);
288 }
289 return ndk::ScopedAStatus::ok();
290 }
291
unregisterCallback(const std::shared_ptr<IHealthInfoCallback> & callback)292 ndk::ScopedAStatus Health::unregisterCallback(
293 const std::shared_ptr<IHealthInfoCallback>& callback) {
294 if (callback == nullptr) {
295 // For now, this shouldn't happen because argument is not nullable.
296 return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
297 }
298
299 std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
300
301 auto matches = [callback](const auto& linked) {
302 return linked->callback()->asBinder() == callback->asBinder(); // compares binder object
303 };
304 auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), matches);
305 bool removed = (it != callbacks_.end());
306 callbacks_.erase(it, callbacks_.end()); // calls unlinkToDeath on deleted callbacks.
307 return removed ? ndk::ScopedAStatus::ok()
308 : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
309 }
310
311 // A combination of the HIDL version
312 // android::hardware::health::V2_1::implementation::Health::update() and
313 // android::hardware::health::V2_1::implementation::BinderHealth::update()
update()314 ndk::ScopedAStatus Health::update() {
315 HealthInfo health_info;
316 if (auto res = getHealthInfo(&health_info); !res.isOk()) {
317 LOG(DEBUG) << "Cannot call getHealthInfo for update(): " << res.getDescription();
318 // Propagate service specific errors. If there's none, report unknown error.
319 if (res.getServiceSpecificError() != 0 ||
320 res.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
321 return res;
322 }
323 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
324 IHealth::STATUS_UNKNOWN, res.getDescription().c_str());
325 }
326 battery_monitor_.logValues();
327 OnHealthInfoChanged(health_info);
328 return ndk::ScopedAStatus::ok();
329 }
330
OnHealthInfoChanged(const HealthInfo & health_info)331 void Health::OnHealthInfoChanged(const HealthInfo& health_info) {
332 // Notify all callbacks
333 std::unique_lock<decltype(callbacks_lock_)> lock(callbacks_lock_);
334 // is_dead notifies a callback and return true if it is dead.
335 auto is_dead = [&](const auto& linked) {
336 auto res = linked->callback()->healthInfoChanged(health_info);
337 return IsDeadObjectLogged(res);
338 };
339 auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), is_dead);
340 callbacks_.erase(it, callbacks_.end()); // calls unlinkToDeath on deleted callbacks.
341 lock.unlock();
342
343 // Let HalHealthLoop::OnHealthInfoChanged() adjusts uevent / wakealarm periods
344 }
345
BinderEvent(uint32_t)346 void Health::BinderEvent(uint32_t /*epevents*/) {
347 if (binder_fd_ >= 0) {
348 ABinderProcess_handlePolledCommands();
349 }
350 }
351
OnInit(HalHealthLoop * hal_health_loop,struct healthd_config * config)352 void Health::OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) {
353 LOG(INFO) << instance_name_ << " instance initializing with healthd_config...";
354
355 // Similar to HIDL's android::hardware::health::V2_1::implementation::HalHealthLoop::Init,
356 // copy configuration parameters to |config| for HealthLoop (e.g. uevent / wake alarm periods)
357 *config = *healthd_config_.get();
358
359 binder_status_t status = ABinderProcess_setupPolling(&binder_fd_);
360
361 if (status == ::STATUS_OK && binder_fd_ >= 0) {
362 std::shared_ptr<Health> thiz = ref<Health>();
363 auto binder_event = [thiz](auto*, uint32_t epevents) { thiz->BinderEvent(epevents); };
364 if (hal_health_loop->RegisterEvent(binder_fd_, binder_event, EVENT_NO_WAKEUP_FD) != 0) {
365 PLOG(ERROR) << instance_name_ << " instance: Register for binder events failed";
366 }
367 }
368
369 std::string health_name = IHealth::descriptor + "/"s + instance_name_;
370 CHECK_EQ(STATUS_OK, AServiceManager_addService(this->asBinder().get(), health_name.c_str()))
371 << instance_name_ << ": Failed to register HAL";
372
373 LOG(INFO) << instance_name_ << ": Hal init done";
374 }
375
376 // Unlike hwbinder, for binder, there's no need to explicitly call flushCommands()
377 // in PrepareToWait(). See b/139697085.
378
379 } // namespace aidl::android::hardware::health
380