xref: /aosp_15_r20/hardware/interfaces/health/aidl/default/Health.cpp (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
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 
getHingeInfo(std::vector<HingeInfo> *)195 ndk::ScopedAStatus Health::getHingeInfo(std::vector<HingeInfo>*) {
196     // This implementation does not support HingeInfo. An implementation may extend this
197     // class and override this function to support storage info.
198     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
199 }
200 
getHealthInfo(HealthInfo * out)201 ndk::ScopedAStatus Health::getHealthInfo(HealthInfo* out) {
202     battery_monitor_.updateValues();
203 
204     *out = battery_monitor_.getHealthInfo();
205 
206     // Fill in storage infos; these aren't retrieved by BatteryMonitor.
207     if (auto res = getStorageInfo(&out->storageInfos); !res.isOk()) {
208         if (res.getServiceSpecificError() == 0 &&
209             res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
210             return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
211                     IHealth::STATUS_UNKNOWN,
212                     ("getStorageInfo fails: " + res.getDescription()).c_str());
213         }
214         LOG(DEBUG) << "getHealthInfo: getStorageInfo fails with service-specific error, clearing: "
215                    << res.getDescription();
216         out->storageInfos = {};
217     }
218     if (auto res = getDiskStats(&out->diskStats); !res.isOk()) {
219         if (res.getServiceSpecificError() == 0 &&
220             res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
221             return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
222                     IHealth::STATUS_UNKNOWN,
223                     ("getDiskStats fails: " + res.getDescription()).c_str());
224         }
225         LOG(DEBUG) << "getHealthInfo: getDiskStats fails with service-specific error, clearing: "
226                    << res.getDescription();
227         out->diskStats = {};
228     }
229 
230     // A subclass may want to update health info struct before returning it.
231     UpdateHealthInfo(out);
232 
233     return ndk::ScopedAStatus::ok();
234 }
235 
dump(int fd,const char **,uint32_t)236 binder_status_t Health::dump(int fd, const char**, uint32_t) {
237     battery_monitor_.dumpState(fd);
238 
239     ::android::base::WriteStringToFd("\ngetHealthInfo -> ", fd);
240     HealthInfo health_info;
241     auto res = getHealthInfo(&health_info);
242     if (res.isOk()) {
243         ::android::base::WriteStringToFd(health_info.toString(), fd);
244     } else {
245         ::android::base::WriteStringToFd(res.getDescription(), fd);
246     }
247     ::android::base::WriteStringToFd("\n", fd);
248 
249     fsync(fd);
250     return STATUS_OK;
251 }
252 
ShouldKeepScreenOn()253 std::optional<bool> Health::ShouldKeepScreenOn() {
254     if (!healthd_config_->screen_on) {
255         return std::nullopt;
256     }
257 
258     HealthInfo health_info;
259     auto res = getHealthInfo(&health_info);
260     if (!res.isOk()) {
261         return std::nullopt;
262     }
263 
264     ::android::BatteryProperties props = {};
265     convert(health_info, &props);
266     return healthd_config_->screen_on(&props);
267 }
268 
269 //
270 // Subclass helpers / overrides
271 //
272 
UpdateHealthInfo(HealthInfo *)273 void Health::UpdateHealthInfo(HealthInfo* /* health_info */) {
274     /*
275         // Sample code for a subclass to implement this:
276         // If you need to modify values (e.g. batteryChargeTimeToFullNowSeconds), do it here.
277         health_info->batteryChargeTimeToFullNowSeconds = calculate_charge_time_seconds();
278 
279         // If you need to call healthd_board_battery_update, modify its signature
280         // and implementation to operate on HealthInfo directly, then call:
281         healthd_board_battery_update(health_info);
282     */
283 }
284 
285 //
286 // Methods that handle callbacks.
287 //
288 
registerCallback(const std::shared_ptr<IHealthInfoCallback> & callback)289 ndk::ScopedAStatus Health::registerCallback(const std::shared_ptr<IHealthInfoCallback>& callback) {
290     if (callback == nullptr) {
291         // For now, this shouldn't happen because argument is not nullable.
292         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
293     }
294 
295     {
296         std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
297         auto linked_callback_result = LinkedCallback::Make(ref<Health>(), callback);
298         if (!linked_callback_result.ok()) {
299             return ndk::ScopedAStatus::fromStatus(-linked_callback_result.error().code());
300         }
301         callbacks_[*linked_callback_result] = callback;
302         // unlock
303     }
304 
305     HealthInfo health_info;
306     if (auto res = getHealthInfo(&health_info); !res.isOk()) {
307         LOG(WARNING) << "Cannot call getHealthInfo: " << res.getDescription();
308         // No health info to send, so return early.
309         return ndk::ScopedAStatus::ok();
310     }
311 
312     auto res = callback->healthInfoChanged(health_info);
313     if (!res.isOk()) {
314         LOG(DEBUG) << "Cannot call healthInfoChanged:" << res.getDescription()
315                    << ". Do nothing here if callback is dead as it will be cleaned up later.";
316     }
317     return ndk::ScopedAStatus::ok();
318 }
319 
unregisterCallback(const std::shared_ptr<IHealthInfoCallback> & callback)320 ndk::ScopedAStatus Health::unregisterCallback(
321         const std::shared_ptr<IHealthInfoCallback>& callback) {
322     if (callback == nullptr) {
323         // For now, this shouldn't happen because argument is not nullable.
324         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
325     }
326 
327     std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
328 
329     auto matches = [callback](const auto& cb) {
330         return cb->asBinder() == callback->asBinder();  // compares binder object
331     };
332     bool removed = false;
333     for (auto it = callbacks_.begin(); it != callbacks_.end();) {
334         if (it->second->asBinder() == callback->asBinder()) {
335             auto status = AIBinder_unlinkToDeath(callback->asBinder().get(), death_recipient_.get(),
336                                                  reinterpret_cast<void*>(it->first));
337             if (status != STATUS_OK && status != STATUS_DEAD_OBJECT) {
338                 LOG(WARNING) << __func__
339                              << "Cannot unlink to death: " << ::android::statusToString(status);
340             }
341             it = callbacks_.erase(it);
342             removed = true;
343         } else {
344             it++;
345         }
346     }
347     return removed ? ndk::ScopedAStatus::ok()
348                    : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
349 }
350 
351 // A combination of the HIDL version
352 //   android::hardware::health::V2_1::implementation::Health::update() and
353 //   android::hardware::health::V2_1::implementation::BinderHealth::update()
update()354 ndk::ScopedAStatus Health::update() {
355     HealthInfo health_info;
356     if (auto res = getHealthInfo(&health_info); !res.isOk()) {
357         LOG(DEBUG) << "Cannot call getHealthInfo for update(): " << res.getDescription();
358         // Propagate service specific errors. If there's none, report unknown error.
359         if (res.getServiceSpecificError() != 0 ||
360             res.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
361             return res;
362         }
363         return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
364                 IHealth::STATUS_UNKNOWN, res.getDescription().c_str());
365     }
366     battery_monitor_.logValues();
367     OnHealthInfoChanged(health_info);
368     return ndk::ScopedAStatus::ok();
369 }
370 
OnHealthInfoChanged(const HealthInfo & health_info)371 void Health::OnHealthInfoChanged(const HealthInfo& health_info) {
372     // Notify all callbacks
373     std::unique_lock<decltype(callbacks_lock_)> lock(callbacks_lock_);
374     for (const auto& [_, callback] : callbacks_) {
375         auto res = callback->healthInfoChanged(health_info);
376         if (!res.isOk()) {
377             LOG(DEBUG) << "Cannot call healthInfoChanged:" << res.getDescription()
378                        << ". Do nothing here if callback is dead as it will be cleaned up later.";
379         }
380     }
381     lock.unlock();
382 
383     // Let HalHealthLoop::OnHealthInfoChanged() adjusts uevent / wakealarm periods
384 }
385 
BinderEvent(uint32_t)386 void Health::BinderEvent(uint32_t /*epevents*/) {
387     if (binder_fd_ >= 0) {
388         ABinderProcess_handlePolledCommands();
389     }
390 }
391 
OnInit(HalHealthLoop * hal_health_loop,struct healthd_config * config)392 void Health::OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) {
393     LOG(INFO) << instance_name_ << " instance initializing with healthd_config...";
394 
395     // Similar to HIDL's android::hardware::health::V2_1::implementation::HalHealthLoop::Init,
396     // copy configuration parameters to |config| for HealthLoop (e.g. uevent / wake alarm periods)
397     *config = *healthd_config_.get();
398 
399     binder_status_t status = ABinderProcess_setupPolling(&binder_fd_);
400 
401     if (status == ::STATUS_OK && binder_fd_ >= 0) {
402         std::shared_ptr<Health> thiz = ref<Health>();
403         auto binder_event = [thiz](auto*, uint32_t epevents) { thiz->BinderEvent(epevents); };
404         if (hal_health_loop->RegisterEvent(binder_fd_, binder_event, EVENT_NO_WAKEUP_FD) != 0) {
405             PLOG(ERROR) << instance_name_ << " instance: Register for binder events failed";
406         }
407     }
408 
409     std::string health_name = IHealth::descriptor + "/"s + instance_name_;
410     CHECK_EQ(STATUS_OK, AServiceManager_addService(this->asBinder().get(), health_name.c_str()))
411             << instance_name_ << ": Failed to register HAL";
412 
413     LOG(INFO) << instance_name_ << ": Hal init done";
414 }
415 
416 // Unlike hwbinder, for binder, there's no need to explicitly call flushCommands()
417 // in PrepareToWait(). See b/139697085.
418 
419 }  // namespace aidl::android::hardware::health
420