1 /*
2  * Copyright (C) 2019 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 <health2impl/Health.h>
18 
19 #include <functional>
20 #include <string_view>
21 
22 #include <android-base/file.h>
23 #include <android-base/logging.h>
24 #include <android/hardware/health/1.0/types.h>
25 #include <android/hardware/health/2.0/IHealthInfoCallback.h>
26 #include <android/hardware/health/2.0/types.h>
27 #include <android/hardware/health/2.1/IHealthInfoCallback.h>
28 #include <hal_conversion.h>
29 #include <healthd/healthd.h>
30 #include <hidl/HidlTransportSupport.h>
31 #include <hwbinder/IPCThreadState.h>
32 
33 #include <health2impl/Callback.h>
34 #include <health2impl/HalHealthLoop.h>
35 
36 using android::hardware::health::V1_0::BatteryStatus;
37 using android::hardware::health::V1_0::toString;
38 using android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
39 using android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
40 using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
41 using android::hardware::health::V2_0::Result;
42 using android::hardware::health::V2_1::IHealth;
43 
44 using ScreenOn = decltype(healthd_config::screen_on);
45 
46 namespace android {
47 namespace hardware {
48 namespace health {
49 namespace V2_1 {
50 namespace implementation {
51 
52 /*
53 // If you need to call healthd_board_init, construct the Health instance with
54 // the healthd_config after calling healthd_board_init:
55 struct healthd_config* init_config(struct healthd_config* config) {
56     healthd_board_init(config);
57     return config;
58 }
59 class MyHealth : public Health {
60     MyHealth(struct healthd_config* config) :
61         Health(init_config(config)) {}
62 };
63 */
64 
Health(std::unique_ptr<healthd_config> && config)65 Health::Health(std::unique_ptr<healthd_config>&& config) : healthd_config_(std::move(config)) {
66     battery_monitor_.init(healthd_config_.get());
67 }
68 
69 //
70 // Callbacks are not supported by the passthrough implementation.
71 //
72 
registerCallback(const sp<V2_0::IHealthInfoCallback> &)73 Return<Result> Health::registerCallback(const sp<V2_0::IHealthInfoCallback>&) {
74     return Result::NOT_SUPPORTED;
75 }
76 
unregisterCallback(const sp<V2_0::IHealthInfoCallback> &)77 Return<Result> Health::unregisterCallback(const sp<V2_0::IHealthInfoCallback>&) {
78     return Result::NOT_SUPPORTED;
79 }
80 
update()81 Return<Result> Health::update() {
82     Result result = Result::UNKNOWN;
83     getHealthInfo_2_1([&](auto res, const auto& /* health_info */) {
84         result = res;
85         if (res != Result::SUCCESS) {
86             LOG(ERROR) << "Cannot call getHealthInfo_2_1: " << toString(res);
87             return;
88         }
89 
90         battery_monitor_.logValues();
91     });
92     return result;
93 }
94 
95 //
96 // Getters.
97 //
98 
99 template <typename T>
GetProperty(BatteryMonitor * monitor,int id,T defaultValue,const std::function<void (Result,T)> & callback)100 static Return<void> GetProperty(BatteryMonitor* monitor, int id, T defaultValue,
101                                 const std::function<void(Result, T)>& callback) {
102     struct BatteryProperty prop;
103     T ret = defaultValue;
104     Result result = Result::SUCCESS;
105     status_t err = monitor->getProperty(static_cast<int>(id), &prop);
106     if (err != OK) {
107         LOG(DEBUG) << "getProperty(" << id << ")"
108                    << " fails: (" << err << ") " << strerror(-err);
109     } else {
110         ret = static_cast<T>(prop.valueInt64);
111     }
112     switch (err) {
113         case OK:
114             result = Result::SUCCESS;
115             break;
116         case NAME_NOT_FOUND:
117             result = Result::NOT_SUPPORTED;
118             break;
119         default:
120             result = Result::UNKNOWN;
121             break;
122     }
123     callback(result, static_cast<T>(ret));
124     return Void();
125 }
126 
getChargeCounter(getChargeCounter_cb _hidl_cb)127 Return<void> Health::getChargeCounter(getChargeCounter_cb _hidl_cb) {
128     return GetProperty<int32_t>(&battery_monitor_, BATTERY_PROP_CHARGE_COUNTER, 0, _hidl_cb);
129 }
130 
getCurrentNow(getCurrentNow_cb _hidl_cb)131 Return<void> Health::getCurrentNow(getCurrentNow_cb _hidl_cb) {
132     return GetProperty<int32_t>(&battery_monitor_, BATTERY_PROP_CURRENT_NOW, 0, _hidl_cb);
133 }
134 
getCurrentAverage(getCurrentAverage_cb _hidl_cb)135 Return<void> Health::getCurrentAverage(getCurrentAverage_cb _hidl_cb) {
136     return GetProperty<int32_t>(&battery_monitor_, BATTERY_PROP_CURRENT_AVG, 0, _hidl_cb);
137 }
138 
getCapacity(getCapacity_cb _hidl_cb)139 Return<void> Health::getCapacity(getCapacity_cb _hidl_cb) {
140     return GetProperty<int32_t>(&battery_monitor_, BATTERY_PROP_CAPACITY, 0, _hidl_cb);
141 }
142 
getEnergyCounter(getEnergyCounter_cb _hidl_cb)143 Return<void> Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) {
144     return GetProperty<int64_t>(&battery_monitor_, BATTERY_PROP_ENERGY_COUNTER, 0, _hidl_cb);
145 }
146 
getChargeStatus(getChargeStatus_cb _hidl_cb)147 Return<void> Health::getChargeStatus(getChargeStatus_cb _hidl_cb) {
148     return GetProperty(&battery_monitor_, BATTERY_PROP_BATTERY_STATUS, BatteryStatus::UNKNOWN,
149                        _hidl_cb);
150 }
151 
getStorageInfo(getStorageInfo_cb _hidl_cb)152 Return<void> Health::getStorageInfo(getStorageInfo_cb _hidl_cb) {
153     // This implementation does not support StorageInfo. An implementation may extend this
154     // class and override this function to support storage info.
155     _hidl_cb(Result::NOT_SUPPORTED, {});
156     return Void();
157 }
158 
getDiskStats(getDiskStats_cb _hidl_cb)159 Return<void> Health::getDiskStats(getDiskStats_cb _hidl_cb) {
160     // This implementation does not support DiskStats. An implementation may extend this
161     // class and override this function to support disk stats.
162     _hidl_cb(Result::NOT_SUPPORTED, {});
163     return Void();
164 }
165 
166 template <typename T, typename Method>
GetHealthInfoField(Health * service,Method func,T * out)167 static inline void GetHealthInfoField(Health* service, Method func, T* out) {
168     *out = T{};
169     std::invoke(func, service, [out](Result result, const T& value) {
170         if (result == Result::SUCCESS) *out = value;
171     });
172 }
173 
getHealthInfo(getHealthInfo_cb _hidl_cb)174 Return<void> Health::getHealthInfo(getHealthInfo_cb _hidl_cb) {
175     return getHealthInfo_2_1(
176             [&](auto res, const auto& health_info) { _hidl_cb(res, health_info.legacy); });
177 }
178 
getHealthInfo_2_1(getHealthInfo_2_1_cb _hidl_cb)179 Return<void> Health::getHealthInfo_2_1(getHealthInfo_2_1_cb _hidl_cb) {
180     battery_monitor_.updateValues();
181 
182     HealthInfo health_info = battery_monitor_.getHealthInfo_2_1();
183 
184     // Fill in storage infos; these aren't retrieved by BatteryMonitor.
185     GetHealthInfoField(this, &Health::getStorageInfo, &health_info.legacy.storageInfos);
186     GetHealthInfoField(this, &Health::getDiskStats, &health_info.legacy.diskStats);
187 
188     // A subclass may want to update health info struct before returning it.
189     UpdateHealthInfo(&health_info);
190 
191     _hidl_cb(Result::SUCCESS, health_info);
192     return Void();
193 }
194 
debug(const hidl_handle & handle,const hidl_vec<hidl_string> &)195 Return<void> Health::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {
196     if (handle == nullptr || handle->numFds == 0) {
197         return Void();
198     }
199 
200     int fd = handle->data[0];
201     battery_monitor_.dumpState(fd);
202     getHealthInfo_2_1([fd](auto res, const auto& info) {
203         android::base::WriteStringToFd("\ngetHealthInfo -> ", fd);
204         if (res == Result::SUCCESS) {
205             android::base::WriteStringToFd(toString(info), fd);
206         } else {
207             android::base::WriteStringToFd(toString(res), fd);
208         }
209         android::base::WriteStringToFd("\n", fd);
210     });
211 
212     fsync(fd);
213     return Void();
214 }
215 
getHealthConfig(getHealthConfig_cb _hidl_cb)216 Return<void> Health::getHealthConfig(getHealthConfig_cb _hidl_cb) {
217     HealthConfig config = {};
218     convertToHealthConfig(healthd_config_.get(), config.battery);
219     config.bootMinCap = static_cast<int32_t>(healthd_config_->boot_min_cap);
220 
221     _hidl_cb(Result::SUCCESS, config);
222     return Void();
223 }
224 
shouldKeepScreenOn(shouldKeepScreenOn_cb _hidl_cb)225 Return<void> Health::shouldKeepScreenOn(shouldKeepScreenOn_cb _hidl_cb) {
226     if (!healthd_config_->screen_on) {
227         _hidl_cb(Result::NOT_SUPPORTED, true);
228         return Void();
229     }
230 
231     Result returned_result = Result::UNKNOWN;
232     bool screen_on = true;
233     getHealthInfo_2_1([&](auto res, const auto& health_info) {
234         returned_result = res;
235         if (returned_result != Result::SUCCESS) return;
236 
237         struct BatteryProperties props = {};
238         V1_0::hal_conversion::convertFromHealthInfo(health_info.legacy.legacy, &props);
239         screen_on = healthd_config_->screen_on(&props);
240     });
241     _hidl_cb(returned_result, screen_on);
242     return Void();
243 }
244 
245 //
246 // Subclass helpers / overrides
247 //
248 
UpdateHealthInfo(HealthInfo *)249 void Health::UpdateHealthInfo(HealthInfo* /* health_info */) {
250     /*
251         // Sample code for a subclass to implement this:
252         // If you need to modify values (e.g. batteryChargeTimeToFullNowSeconds), do it here.
253         health_info->batteryChargeTimeToFullNowSeconds = calculate_charge_time_seconds();
254 
255         // If you need to call healthd_board_battery_update:
256         struct BatteryProperties props;
257         convertFromHealthInfo(health_info.legacy.legacy, &props);
258         healthd_board_battery_update(&props);
259         convertToHealthInfo(&props, health_info.legacy.legacy);
260     */
261 }
262 
263 }  // namespace implementation
264 }  // namespace V2_1
265 }  // namespace health
266 }  // namespace hardware
267 }  // namespace android
268