• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
getDiskStats(std::vector<DiskStats> *)118 ndk::ScopedAStatus Health::getDiskStats(std::vector<DiskStats>*) {
119     // This implementation does not support DiskStats. An implementation may extend this
120     // class and override this function to support disk stats.
121     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
122 }
123 
getStorageInfo(std::vector<StorageInfo> *)124 ndk::ScopedAStatus Health::getStorageInfo(std::vector<StorageInfo>*) {
125     // This implementation does not support StorageInfo. An implementation may extend this
126     // class and override this function to support storage info.
127     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
128 }
129 
getHealthInfo(HealthInfo * out)130 ndk::ScopedAStatus Health::getHealthInfo(HealthInfo* out) {
131     battery_monitor_.updateValues();
132 
133     *out = battery_monitor_.getHealthInfo();
134 
135     // Fill in storage infos; these aren't retrieved by BatteryMonitor.
136     if (auto res = getStorageInfo(&out->storageInfos); !res.isOk()) {
137         if (res.getServiceSpecificError() == 0 &&
138             res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
139             return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
140                     IHealth::STATUS_UNKNOWN,
141                     ("getStorageInfo fails: " + res.getDescription()).c_str());
142         }
143         LOG(DEBUG) << "getHealthInfo: getStorageInfo fails with service-specific error, clearing: "
144                    << res.getDescription();
145         out->storageInfos = {};
146     }
147     if (auto res = getDiskStats(&out->diskStats); !res.isOk()) {
148         if (res.getServiceSpecificError() == 0 &&
149             res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
150             return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
151                     IHealth::STATUS_UNKNOWN,
152                     ("getDiskStats fails: " + res.getDescription()).c_str());
153         }
154         LOG(DEBUG) << "getHealthInfo: getDiskStats fails with service-specific error, clearing: "
155                    << res.getDescription();
156         out->diskStats = {};
157     }
158 
159     // A subclass may want to update health info struct before returning it.
160     UpdateHealthInfo(out);
161 
162     return ndk::ScopedAStatus::ok();
163 }
164 
dump(int fd,const char **,uint32_t)165 binder_status_t Health::dump(int fd, const char**, uint32_t) {
166     battery_monitor_.dumpState(fd);
167 
168     ::android::base::WriteStringToFd("\ngetHealthInfo -> ", fd);
169     HealthInfo health_info;
170     auto res = getHealthInfo(&health_info);
171     if (res.isOk()) {
172         ::android::base::WriteStringToFd(health_info.toString(), fd);
173     } else {
174         ::android::base::WriteStringToFd(res.getDescription(), fd);
175     }
176 
177     fsync(fd);
178     return STATUS_OK;
179 }
180 
ShouldKeepScreenOn()181 std::optional<bool> Health::ShouldKeepScreenOn() {
182     if (!healthd_config_->screen_on) {
183         return std::nullopt;
184     }
185 
186     HealthInfo health_info;
187     auto res = getHealthInfo(&health_info);
188     if (!res.isOk()) {
189         return std::nullopt;
190     }
191 
192     ::android::BatteryProperties props = {};
193     convert(health_info, &props);
194     return healthd_config_->screen_on(&props);
195 }
196 
197 namespace {
IsDeadObjectLogged(const ndk::ScopedAStatus & ret)198 bool IsDeadObjectLogged(const ndk::ScopedAStatus& ret) {
199     if (ret.isOk()) return false;
200     if (ret.getStatus() == ::STATUS_DEAD_OBJECT) return true;
201     LOG(ERROR) << "Cannot call healthInfoChanged on callback: " << ret.getDescription();
202     return false;
203 }
204 }  // namespace
205 
206 //
207 // Subclass helpers / overrides
208 //
209 
UpdateHealthInfo(HealthInfo *)210 void Health::UpdateHealthInfo(HealthInfo* /* health_info */) {
211     /*
212         // Sample code for a subclass to implement this:
213         // If you need to modify values (e.g. batteryChargeTimeToFullNowSeconds), do it here.
214         health_info->batteryChargeTimeToFullNowSeconds = calculate_charge_time_seconds();
215 
216         // If you need to call healthd_board_battery_update, modify its signature
217         // and implementation to operate on HealthInfo directly, then call:
218         healthd_board_battery_update(health_info);
219     */
220 }
221 
222 //
223 // Methods that handle callbacks.
224 //
225 
registerCallback(const std::shared_ptr<IHealthInfoCallback> & callback)226 ndk::ScopedAStatus Health::registerCallback(const std::shared_ptr<IHealthInfoCallback>& callback) {
227     if (callback == nullptr) {
228         // For now, this shouldn't happen because argument is not nullable.
229         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
230     }
231 
232     {
233         std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
234         callbacks_.emplace_back(LinkedCallback::Make(ref<Health>(), callback));
235         // unlock
236     }
237 
238     HealthInfo health_info;
239     if (auto res = getHealthInfo(&health_info); !res.isOk()) {
240         LOG(WARNING) << "Cannot call getHealthInfo: " << res.getDescription();
241         // No health info to send, so return early.
242         return ndk::ScopedAStatus::ok();
243     }
244 
245     if (auto res = callback->healthInfoChanged(health_info); IsDeadObjectLogged(res)) {
246         (void)unregisterCallback(callback);
247     }
248     return ndk::ScopedAStatus::ok();
249 }
250 
unregisterCallback(const std::shared_ptr<IHealthInfoCallback> & callback)251 ndk::ScopedAStatus Health::unregisterCallback(
252         const std::shared_ptr<IHealthInfoCallback>& callback) {
253     if (callback == nullptr) {
254         // For now, this shouldn't happen because argument is not nullable.
255         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
256     }
257 
258     std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
259 
260     auto matches = [callback](const auto& linked) {
261         return linked->callback()->asBinder() == callback->asBinder();  // compares binder object
262     };
263     auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), matches);
264     bool removed = (it != callbacks_.end());
265     callbacks_.erase(it, callbacks_.end());  // calls unlinkToDeath on deleted callbacks.
266     return removed ? ndk::ScopedAStatus::ok()
267                    : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
268 }
269 
270 // A combination of the HIDL version
271 //   android::hardware::health::V2_1::implementation::Health::update() and
272 //   android::hardware::health::V2_1::implementation::BinderHealth::update()
update()273 ndk::ScopedAStatus Health::update() {
274     HealthInfo health_info;
275     if (auto res = getHealthInfo(&health_info); !res.isOk()) {
276         LOG(DEBUG) << "Cannot call getHealthInfo for update(): " << res.getDescription();
277         // Propagate service specific errors. If there's none, report unknown error.
278         if (res.getServiceSpecificError() != 0 ||
279             res.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
280             return res;
281         }
282         return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
283                 IHealth::STATUS_UNKNOWN, res.getDescription().c_str());
284     }
285     battery_monitor_.logValues();
286     OnHealthInfoChanged(health_info);
287     return ndk::ScopedAStatus::ok();
288 }
289 
OnHealthInfoChanged(const HealthInfo & health_info)290 void Health::OnHealthInfoChanged(const HealthInfo& health_info) {
291     // Notify all callbacks
292     std::unique_lock<decltype(callbacks_lock_)> lock(callbacks_lock_);
293     // is_dead notifies a callback and return true if it is dead.
294     auto is_dead = [&](const auto& linked) {
295         auto res = linked->callback()->healthInfoChanged(health_info);
296         return IsDeadObjectLogged(res);
297     };
298     auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), is_dead);
299     callbacks_.erase(it, callbacks_.end());  // calls unlinkToDeath on deleted callbacks.
300     lock.unlock();
301 
302     // Let HalHealthLoop::OnHealthInfoChanged() adjusts uevent / wakealarm periods
303 }
304 
BinderEvent(uint32_t)305 void Health::BinderEvent(uint32_t /*epevents*/) {
306     if (binder_fd_ >= 0) {
307         ABinderProcess_handlePolledCommands();
308     }
309 }
310 
OnInit(HalHealthLoop * hal_health_loop,struct healthd_config * config)311 void Health::OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) {
312     LOG(INFO) << instance_name_ << " instance initializing with healthd_config...";
313 
314     // Similar to HIDL's android::hardware::health::V2_1::implementation::HalHealthLoop::Init,
315     // copy configuration parameters to |config| for HealthLoop (e.g. uevent / wake alarm periods)
316     *config = *healthd_config_.get();
317 
318     binder_status_t status = ABinderProcess_setupPolling(&binder_fd_);
319 
320     if (status == ::STATUS_OK && binder_fd_ >= 0) {
321         std::shared_ptr<Health> thiz = ref<Health>();
322         auto binder_event = [thiz](auto*, uint32_t epevents) { thiz->BinderEvent(epevents); };
323         if (hal_health_loop->RegisterEvent(binder_fd_, binder_event, EVENT_NO_WAKEUP_FD) != 0) {
324             PLOG(ERROR) << instance_name_ << " instance: Register for binder events failed";
325         }
326     }
327 
328     std::string health_name = IHealth::descriptor + "/"s + instance_name_;
329     CHECK_EQ(STATUS_OK, AServiceManager_addService(this->asBinder().get(), health_name.c_str()))
330             << instance_name_ << ": Failed to register HAL";
331 
332     LOG(INFO) << instance_name_ << ": Hal init done";
333 }
334 
335 // Unlike hwbinder, for binder, there's no need to explicitly call flushCommands()
336 // in PrepareToWait(). See b/139697085.
337 
338 }  // namespace aidl::android::hardware::health
339