blob: 2e0cfc97182ad283247ef41b289be19e55fe1624 [file] [log] [blame]
Jack Wu2ed5c9c2023-01-21 14:33:55 +08001/*
2 * Copyright (C) 2013 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#define LOG_TAG "healthd"
18
19#include <healthd/healthd.h>
20#include <healthd/BatteryMonitor_v1.h>
21
22#include <dirent.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <sys/types.h>
28#include <unistd.h>
29
30#include <algorithm>
31#include <memory>
32#include <optional>
33
34#include <aidl/android/hardware/health/HealthInfo.h>
35#include <android-base/file.h>
36#include <android-base/parseint.h>
37#include <android-base/strings.h>
38#include <android/hardware/health/2.1/types.h>
39#include <android/hardware/health/translate-ndk.h>
40#include <batteryservice/BatteryService.h>
41#include <cutils/klog.h>
42#include <cutils/properties.h>
43#include <utils/Errors.h>
44#include <utils/String8.h>
45#include <utils/Vector.h>
46
47#define POWER_SUPPLY_SUBSYSTEM "power_supply"
48#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
49#define FAKE_BATTERY_CAPACITY 42
50#define FAKE_BATTERY_TEMPERATURE 424
51#define MILLION 1.0e6
52#define DEFAULT_VBUS_VOLTAGE 5000000
53
54using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
55using HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;
56using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
57using aidl::android::hardware::health::BatteryCapacityLevel;
58using aidl::android::hardware::health::BatteryHealth;
59using aidl::android::hardware::health::BatteryStatus;
60using aidl::android::hardware::health::HealthInfo;
61
62namespace {
63
64// Translate from AIDL back to HIDL definition for getHealthInfo_*_* calls.
65// Skips storageInfo and diskStats.
66void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
67 ::android::hardware::health::V1_0::HealthInfo* out) {
68 out->chargerAcOnline = in.chargerAcOnline;
69 out->chargerUsbOnline = in.chargerUsbOnline;
70 out->chargerWirelessOnline = in.chargerWirelessOnline;
71 out->maxChargingCurrent = in.maxChargingCurrentMicroamps;
72 out->maxChargingVoltage = in.maxChargingVoltageMicrovolts;
73 out->batteryStatus =
74 static_cast<::android::hardware::health::V1_0::BatteryStatus>(in.batteryStatus);
75 out->batteryHealth =
76 static_cast<::android::hardware::health::V1_0::BatteryHealth>(in.batteryHealth);
77 out->batteryPresent = in.batteryPresent;
78 out->batteryLevel = in.batteryLevel;
79 out->batteryVoltage = in.batteryVoltageMillivolts;
80 out->batteryTemperature = in.batteryTemperatureTenthsCelsius;
81 out->batteryCurrent = in.batteryCurrentMicroamps;
82 out->batteryCycleCount = in.batteryCycleCount;
83 out->batteryFullCharge = in.batteryFullChargeUah;
84 out->batteryChargeCounter = in.batteryChargeCounterUah;
85 out->batteryTechnology = in.batteryTechnology;
86}
87
88void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
89 ::android::hardware::health::V2_0::HealthInfo* out) {
90 translateToHidl(in, &out->legacy);
91 out->batteryCurrentAverage = in.batteryCurrentAverageMicroamps;
92 // Skip storageInfo and diskStats
93}
94
95void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
96 ::android::hardware::health::V2_1::HealthInfo* out) {
97 translateToHidl(in, &out->legacy);
98 out->batteryCapacityLevel = static_cast<android::hardware::health::V2_1::BatteryCapacityLevel>(
99 in.batteryCapacityLevel);
100 out->batteryChargeTimeToFullNowSeconds = in.batteryChargeTimeToFullNowSeconds;
101 out->batteryFullChargeDesignCapacityUah = in.batteryFullChargeDesignCapacityUah;
102}
103
104} // namespace
105
106namespace android {
107
108template <typename T>
109struct SysfsStringEnumMap {
110 const char* s;
111 T val;
112};
113
114template <typename T>
115static std::optional<T> mapSysfsString(const char* str, SysfsStringEnumMap<T> map[]) {
116 for (int i = 0; map[i].s; i++)
117 if (!strcmp(str, map[i].s))
118 return map[i].val;
119
120 return std::nullopt;
121}
122
123static void initHealthInfo(HealthInfo* health_info) {
124 *health_info = {
125 .batteryCapacityLevel = BatteryCapacityLevel::UNSUPPORTED,
126 .batteryChargeTimeToFullNowSeconds =
127 (int64_t)HealthInfo::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED,
128 .batteryStatus = BatteryStatus::UNKNOWN,
129 .batteryHealth = BatteryHealth::UNKNOWN,
130 };
131}
132
133BatteryMonitor::BatteryMonitor()
134 : mHealthdConfig(nullptr),
135 mBatteryDevicePresent(false),
136 mBatteryFixedCapacity(0),
137 mBatteryFixedTemperature(0),
138 mHealthInfo(std::make_unique<HealthInfo>()) {
139 initHealthInfo(mHealthInfo.get());
140}
141
142BatteryMonitor::~BatteryMonitor() {}
143
144HealthInfo_1_0 BatteryMonitor::getHealthInfo_1_0() const {
145 HealthInfo_1_0 health_info_1_0;
146 translateToHidl(*mHealthInfo, &health_info_1_0);
147 return health_info_1_0;
148}
149
150HealthInfo_2_0 BatteryMonitor::getHealthInfo_2_0() const {
151 HealthInfo_2_0 health_info_2_0;
152 translateToHidl(*mHealthInfo, &health_info_2_0);
153 return health_info_2_0;
154}
155
156HealthInfo_2_1 BatteryMonitor::getHealthInfo_2_1() const {
157 HealthInfo_2_1 health_info_2_1;
158 translateToHidl(*mHealthInfo, &health_info_2_1);
159 return health_info_2_1;
160}
161
162const HealthInfo& BatteryMonitor::getHealthInfo() const {
163 return *mHealthInfo;
164}
165
166BatteryStatus getBatteryStatus(const char* status) {
167 static SysfsStringEnumMap<BatteryStatus> batteryStatusMap[] = {
168 {"Unknown", BatteryStatus::UNKNOWN},
169 {"Charging", BatteryStatus::CHARGING},
170 {"Discharging", BatteryStatus::DISCHARGING},
171 {"Not charging", BatteryStatus::NOT_CHARGING},
172 {"Full", BatteryStatus::FULL},
173 {NULL, BatteryStatus::UNKNOWN},
174 };
175
176 auto ret = mapSysfsString(status, batteryStatusMap);
177 if (!ret) {
178 KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status);
179 *ret = BatteryStatus::UNKNOWN;
180 }
181
182 return *ret;
183}
184
185BatteryCapacityLevel getBatteryCapacityLevel(const char* capacityLevel) {
186 static SysfsStringEnumMap<BatteryCapacityLevel> batteryCapacityLevelMap[] = {
187 {"Unknown", BatteryCapacityLevel::UNKNOWN},
188 {"Critical", BatteryCapacityLevel::CRITICAL},
189 {"Low", BatteryCapacityLevel::LOW},
190 {"Normal", BatteryCapacityLevel::NORMAL},
191 {"High", BatteryCapacityLevel::HIGH},
192 {"Full", BatteryCapacityLevel::FULL},
193 {NULL, BatteryCapacityLevel::UNSUPPORTED},
194 };
195
196 auto ret = mapSysfsString(capacityLevel, batteryCapacityLevelMap);
197 if (!ret) {
198 KLOG_WARNING(LOG_TAG, "Unsupported battery capacity level '%s'\n", capacityLevel);
199 *ret = BatteryCapacityLevel::UNSUPPORTED;
200 }
201
202 return *ret;
203}
204
205BatteryHealth getBatteryHealth(const char* status) {
206 static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {
207 {"Unknown", BatteryHealth::UNKNOWN},
208 {"Good", BatteryHealth::GOOD},
209 {"Overheat", BatteryHealth::OVERHEAT},
210 {"Dead", BatteryHealth::DEAD},
211 {"Over voltage", BatteryHealth::OVER_VOLTAGE},
212 {"Unspecified failure", BatteryHealth::UNSPECIFIED_FAILURE},
213 {"Cold", BatteryHealth::COLD},
214 // battery health values from JEITA spec
215 {"Warm", BatteryHealth::GOOD},
216 {"Cool", BatteryHealth::GOOD},
217 {"Hot", BatteryHealth::OVERHEAT},
218 {NULL, BatteryHealth::UNKNOWN},
219 };
220
221 auto ret = mapSysfsString(status, batteryHealthMap);
222 if (!ret) {
223 KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status);
224 *ret = BatteryHealth::UNKNOWN;
225 }
226
227 return *ret;
228}
229
230static int readFromFile(const String8& path, std::string* buf) {
231 buf->clear();
232 if (android::base::ReadFileToString(path.c_str(), buf)) {
233 *buf = android::base::Trim(*buf);
234 }
235 return buf->length();
236}
237
238static BatteryMonitor::PowerSupplyType readPowerSupplyType(const String8& path) {
239 static SysfsStringEnumMap<int> supplyTypeMap[] = {
240 {"Unknown", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
241 {"Battery", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_BATTERY},
242 {"UPS", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
243 {"Mains", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
244 {"USB", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},
245 {"USB_DCP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
246 {"USB_HVDCP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
247 {"USB_CDP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
248 {"USB_ACA", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
249 {"USB_C", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
250 {"USB_PD", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
251 {"USB_PD_DRP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},
252 {"Wireless", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
253 {"Dock", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_DOCK},
254 {NULL, 0},
255 };
256 std::string buf;
257
258 if (readFromFile(path, &buf) <= 0) {
259 return BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
260 }
261
262 auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);
263 if (!ret) {
264 KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
265 *ret = BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
266 }
267
268 return static_cast<BatteryMonitor::PowerSupplyType>(*ret);
269}
270
271static bool getBooleanField(const String8& path) {
272 std::string buf;
273 bool value = false;
274
275 if (readFromFile(path, &buf) > 0)
276 if (buf[0] != '0')
277 value = true;
278
279 return value;
280}
281
282static int getIntField(const String8& path) {
283 std::string buf;
284 int value = 0;
285
286 if (readFromFile(path, &buf) > 0)
287 android::base::ParseInt(buf, &value);
288
289 return value;
290}
291
292static bool isScopedPowerSupply(const char* name) {
293 constexpr char kScopeDevice[] = "Device";
294
295 String8 path;
296 path.appendFormat("%s/%s/scope", POWER_SUPPLY_SYSFS_PATH, name);
297 std::string scope;
298 return (readFromFile(path, &scope) > 0 && scope == kScopeDevice);
299}
300
301void BatteryMonitor::updateValues(void) {
302 initHealthInfo(mHealthInfo.get());
303
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000304 if (!mHealthdConfig->batteryPresentPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800305 mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
306 else
307 mHealthInfo->batteryPresent = mBatteryDevicePresent;
308
309 mHealthInfo->batteryLevel = mBatteryFixedCapacity
310 ? mBatteryFixedCapacity
311 : getIntField(mHealthdConfig->batteryCapacityPath);
312 mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
313
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000314 if (!mHealthdConfig->batteryCurrentNowPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800315 mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);
316
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000317 if (!mHealthdConfig->batteryFullChargePath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800318 mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);
319
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000320 if (!mHealthdConfig->batteryCycleCountPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800321 mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
322
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000323 if (!mHealthdConfig->batteryChargeCounterPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800324 mHealthInfo->batteryChargeCounterUah =
325 getIntField(mHealthdConfig->batteryChargeCounterPath);
326
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000327 if (!mHealthdConfig->batteryCurrentAvgPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800328 mHealthInfo->batteryCurrentAverageMicroamps =
329 getIntField(mHealthdConfig->batteryCurrentAvgPath);
330
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000331 if (!mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800332 mHealthInfo->batteryChargeTimeToFullNowSeconds =
333 getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
334
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000335 if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800336 mHealthInfo->batteryFullChargeDesignCapacityUah =
337 getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
338
339 mHealthInfo->batteryTemperatureTenthsCelsius =
340 mBatteryFixedTemperature ? mBatteryFixedTemperature
341 : getIntField(mHealthdConfig->batteryTemperaturePath);
342
343 std::string buf;
344
345 if (readFromFile(mHealthdConfig->batteryCapacityLevelPath, &buf) > 0)
346 mHealthInfo->batteryCapacityLevel = getBatteryCapacityLevel(buf.c_str());
347
348 if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
349 mHealthInfo->batteryStatus = getBatteryStatus(buf.c_str());
350
351 if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
352 mHealthInfo->batteryHealth = getBatteryHealth(buf.c_str());
353
354 if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000355 mHealthInfo->batteryTechnology = buf;
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800356
357 double MaxPower = 0;
358
359 for (size_t i = 0; i < mChargerNames.size(); i++) {
360 String8 path;
Tomasz Wasilczyk18b74612023-08-10 23:29:50 +0000361 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800362 if (getIntField(path)) {
363 path.clear();
Tomasz Wasilczyk18b74612023-08-10 23:29:50 +0000364 path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800365 switch(readPowerSupplyType(path)) {
366 case ANDROID_POWER_SUPPLY_TYPE_AC:
367 mHealthInfo->chargerAcOnline = true;
368 break;
369 case ANDROID_POWER_SUPPLY_TYPE_USB:
370 mHealthInfo->chargerUsbOnline = true;
371 break;
372 case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
373 mHealthInfo->chargerWirelessOnline = true;
374 break;
375 case ANDROID_POWER_SUPPLY_TYPE_DOCK:
376 mHealthInfo->chargerDockOnline = true;
377 break;
378 default:
379 path.clear();
380 path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
Tomasz Wasilczyk18b74612023-08-10 23:29:50 +0000381 mChargerNames[i].c_str());
382 if (access(path.c_str(), R_OK) == 0)
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800383 mHealthInfo->chargerDockOnline = true;
384 else
385 KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
Tomasz Wasilczyk18b74612023-08-10 23:29:50 +0000386 mChargerNames[i].c_str());
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800387 }
388 path.clear();
389 path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
Tomasz Wasilczyk18b74612023-08-10 23:29:50 +0000390 mChargerNames[i].c_str());
391 int ChargingCurrent = (access(path.c_str(), R_OK) == 0) ? getIntField(path) : 0;
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800392
393 path.clear();
394 path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
Tomasz Wasilczyk18b74612023-08-10 23:29:50 +0000395 mChargerNames[i].c_str());
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800396
397 int ChargingVoltage =
Tomasz Wasilczyk18b74612023-08-10 23:29:50 +0000398 (access(path.c_str(), R_OK) == 0) ? getIntField(path) : DEFAULT_VBUS_VOLTAGE;
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800399
400 double power = ((double)ChargingCurrent / MILLION) *
401 ((double)ChargingVoltage / MILLION);
402 if (MaxPower < power) {
403 mHealthInfo->maxChargingCurrentMicroamps = ChargingCurrent;
404 mHealthInfo->maxChargingVoltageMicrovolts = ChargingVoltage;
405 MaxPower = power;
406 }
407 }
408 }
409}
410
411static void doLogValues(const HealthInfo& props, const struct healthd_config& healthd_config) {
412 char dmesgline[256];
413 size_t len;
414 if (props.batteryPresent) {
415 snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
416 props.batteryLevel, props.batteryVoltageMillivolts,
417 props.batteryTemperatureTenthsCelsius < 0 ? "-" : "",
418 abs(props.batteryTemperatureTenthsCelsius / 10),
419 abs(props.batteryTemperatureTenthsCelsius % 10), props.batteryHealth,
420 props.batteryStatus);
421
422 len = strlen(dmesgline);
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000423 if (!healthd_config.batteryCurrentNowPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800424 len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
425 props.batteryCurrentMicroamps);
426 }
427
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000428 if (!healthd_config.batteryFullChargePath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800429 len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
430 props.batteryFullChargeUah);
431 }
432
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000433 if (!healthd_config.batteryCycleCountPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800434 len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
435 props.batteryCycleCount);
436 }
437 } else {
438 len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
439 }
440
441 snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s%s",
442 props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "",
443 props.chargerWirelessOnline ? "w" : "", props.chargerDockOnline ? "d" : "");
444
445 KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
446}
447
448void BatteryMonitor::logValues(const HealthInfo_2_1& health_info,
449 const struct healthd_config& healthd_config) {
450 HealthInfo aidl_health_info;
451 (void)android::h2a::translate(health_info, &aidl_health_info);
452 doLogValues(aidl_health_info, healthd_config);
453}
454
455void BatteryMonitor::logValues(void) {
456 doLogValues(*mHealthInfo, *mHealthdConfig);
457}
458
459bool BatteryMonitor::isChargerOnline() {
460 const HealthInfo& props = *mHealthInfo;
461 return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline |
462 props.chargerDockOnline;
463}
464
465int BatteryMonitor::getChargeStatus() {
466 BatteryStatus result = BatteryStatus::UNKNOWN;
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000467 if (!mHealthdConfig->batteryStatusPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800468 std::string buf;
469 if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
470 result = getBatteryStatus(buf.c_str());
471 }
472 return static_cast<int>(result);
473}
474
475status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
476 status_t ret = BAD_VALUE;
477 std::string buf;
478
479 val->valueInt64 = LONG_MIN;
480
481 switch(id) {
482 case BATTERY_PROP_CHARGE_COUNTER:
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000483 if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800484 val->valueInt64 =
485 getIntField(mHealthdConfig->batteryChargeCounterPath);
486 ret = OK;
487 } else {
488 ret = NAME_NOT_FOUND;
489 }
490 break;
491
492 case BATTERY_PROP_CURRENT_NOW:
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000493 if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800494 val->valueInt64 =
495 getIntField(mHealthdConfig->batteryCurrentNowPath);
496 ret = OK;
497 } else {
498 ret = NAME_NOT_FOUND;
499 }
500 break;
501
502 case BATTERY_PROP_CURRENT_AVG:
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000503 if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800504 val->valueInt64 =
505 getIntField(mHealthdConfig->batteryCurrentAvgPath);
506 ret = OK;
507 } else {
508 ret = NAME_NOT_FOUND;
509 }
510 break;
511
512 case BATTERY_PROP_CAPACITY:
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000513 if (!mHealthdConfig->batteryCapacityPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800514 val->valueInt64 =
515 getIntField(mHealthdConfig->batteryCapacityPath);
516 ret = OK;
517 } else {
518 ret = NAME_NOT_FOUND;
519 }
520 break;
521
522 case BATTERY_PROP_ENERGY_COUNTER:
523 if (mHealthdConfig->energyCounter) {
524 ret = mHealthdConfig->energyCounter(&val->valueInt64);
525 } else {
526 ret = NAME_NOT_FOUND;
527 }
528 break;
529
530 case BATTERY_PROP_BATTERY_STATUS:
531 val->valueInt64 = getChargeStatus();
532 ret = OK;
533 break;
534
535 default:
536 break;
537 }
538
539 return ret;
540}
541
542void BatteryMonitor::dumpState(int fd) {
543 int v;
544 char vs[128];
545 const HealthInfo& props = *mHealthInfo;
546
547 snprintf(vs, sizeof(vs),
548 "ac: %d usb: %d wireless: %d dock: %d current_max: %d voltage_max: %d\n",
549 props.chargerAcOnline, props.chargerUsbOnline, props.chargerWirelessOnline,
550 props.chargerDockOnline, props.maxChargingCurrentMicroamps,
551 props.maxChargingVoltageMicrovolts);
552 write(fd, vs, strlen(vs));
553 snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
554 props.batteryStatus, props.batteryHealth, props.batteryPresent);
555 write(fd, vs, strlen(vs));
556 snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n", props.batteryLevel,
557 props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);
558 write(fd, vs, strlen(vs));
559
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000560 if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800561 v = getIntField(mHealthdConfig->batteryCurrentNowPath);
562 snprintf(vs, sizeof(vs), "current now: %d\n", v);
563 write(fd, vs, strlen(vs));
564 }
565
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000566 if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800567 v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
568 snprintf(vs, sizeof(vs), "current avg: %d\n", v);
569 write(fd, vs, strlen(vs));
570 }
571
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000572 if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800573 v = getIntField(mHealthdConfig->batteryChargeCounterPath);
574 snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
575 write(fd, vs, strlen(vs));
576 }
577
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000578 if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800579 snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrentMicroamps);
580 write(fd, vs, strlen(vs));
581 }
582
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000583 if (!mHealthdConfig->batteryCycleCountPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800584 snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
585 write(fd, vs, strlen(vs));
586 }
587
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000588 if (!mHealthdConfig->batteryFullChargePath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800589 snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullChargeUah);
590 write(fd, vs, strlen(vs));
591 }
592}
593
594void BatteryMonitor::init(struct healthd_config *hc) {
595 String8 path;
596 char pval[PROPERTY_VALUE_MAX];
597
598 mHealthdConfig = hc;
599 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);
600 if (dir == NULL) {
601 KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
602 } else {
603 struct dirent* entry;
604
605 while ((entry = readdir(dir.get()))) {
606 const char* name = entry->d_name;
607
608 if (!strcmp(name, ".") || !strcmp(name, ".."))
609 continue;
610
611 std::vector<String8>::iterator itIgnoreName =
612 find(hc->ignorePowerSupplyNames.begin(), hc->ignorePowerSupplyNames.end(),
613 String8(name));
614 if (itIgnoreName != hc->ignorePowerSupplyNames.end())
615 continue;
616
617 // Look for "type" file in each subdirectory
618 path.clear();
619 path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
620 switch(readPowerSupplyType(path)) {
621 case ANDROID_POWER_SUPPLY_TYPE_AC:
622 case ANDROID_POWER_SUPPLY_TYPE_USB:
623 case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
624 case ANDROID_POWER_SUPPLY_TYPE_DOCK:
625 path.clear();
626 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
Tomasz Wasilczyk18b74612023-08-10 23:29:50 +0000627 if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800628 break;
629
630 case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
631 // Some devices expose the battery status of sub-component like
632 // stylus. Such a device-scoped battery info needs to be skipped
633 // in BatteryMonitor, which is intended to report the status of
634 // the battery supplying the power to the whole system.
635 if (isScopedPowerSupply(name)) continue;
636 mBatteryDevicePresent = true;
637
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000638 if (mHealthdConfig->batteryStatusPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800639 path.clear();
640 path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
641 name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000642 if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryStatusPath = path;
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800643 }
644
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000645 if (mHealthdConfig->batteryHealthPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800646 path.clear();
647 path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
648 name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000649 if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryHealthPath = path;
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800650 }
651
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000652 if (mHealthdConfig->batteryPresentPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800653 path.clear();
654 path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
655 name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000656 if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryPresentPath = path;
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800657 }
658
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000659 if (mHealthdConfig->batteryCapacityPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800660 path.clear();
661 path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
662 name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000663 if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryCapacityPath = path;
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800664 }
665
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000666 if (mHealthdConfig->batteryVoltagePath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800667 path.clear();
668 path.appendFormat("%s/%s/voltage_now",
669 POWER_SUPPLY_SYSFS_PATH, name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000670 if (access(path.c_str(), R_OK) == 0) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800671 mHealthdConfig->batteryVoltagePath = path;
672 }
673 }
674
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000675 if (mHealthdConfig->batteryFullChargePath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800676 path.clear();
677 path.appendFormat("%s/%s/charge_full",
678 POWER_SUPPLY_SYSFS_PATH, name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000679 if (access(path.c_str(), R_OK) == 0)
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800680 mHealthdConfig->batteryFullChargePath = path;
681 }
682
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000683 if (mHealthdConfig->batteryCurrentNowPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800684 path.clear();
685 path.appendFormat("%s/%s/current_now",
686 POWER_SUPPLY_SYSFS_PATH, name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000687 if (access(path.c_str(), R_OK) == 0)
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800688 mHealthdConfig->batteryCurrentNowPath = path;
689 }
690
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000691 if (mHealthdConfig->batteryCycleCountPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800692 path.clear();
693 path.appendFormat("%s/%s/cycle_count",
694 POWER_SUPPLY_SYSFS_PATH, name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000695 if (access(path.c_str(), R_OK) == 0)
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800696 mHealthdConfig->batteryCycleCountPath = path;
697 }
698
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000699 if (mHealthdConfig->batteryCapacityLevelPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800700 path.clear();
701 path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000702 if (access(path.c_str(), R_OK) == 0) {
703 mHealthdConfig->batteryCapacityLevelPath = path;
704 }
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800705 }
706
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000707 if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800708 path.clear();
709 path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000710 if (access(path.c_str(), R_OK) == 0)
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800711 mHealthdConfig->batteryChargeTimeToFullNowPath = path;
712 }
713
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000714 if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800715 path.clear();
716 path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000717 if (access(path.c_str(), R_OK) == 0)
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800718 mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
719 }
720
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000721 if (mHealthdConfig->batteryCurrentAvgPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800722 path.clear();
723 path.appendFormat("%s/%s/current_avg",
724 POWER_SUPPLY_SYSFS_PATH, name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000725 if (access(path.c_str(), R_OK) == 0)
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800726 mHealthdConfig->batteryCurrentAvgPath = path;
727 }
728
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000729 if (mHealthdConfig->batteryChargeCounterPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800730 path.clear();
731 path.appendFormat("%s/%s/charge_counter",
732 POWER_SUPPLY_SYSFS_PATH, name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000733 if (access(path.c_str(), R_OK) == 0)
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800734 mHealthdConfig->batteryChargeCounterPath = path;
735 }
736
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000737 if (mHealthdConfig->batteryTemperaturePath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800738 path.clear();
739 path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
740 name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000741 if (access(path.c_str(), R_OK) == 0) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800742 mHealthdConfig->batteryTemperaturePath = path;
743 }
744 }
745
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000746 if (mHealthdConfig->batteryTechnologyPath.empty()) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800747 path.clear();
748 path.appendFormat("%s/%s/technology",
749 POWER_SUPPLY_SYSFS_PATH, name);
Tomasz Wasilczyk2b1a0592023-09-12 15:26:15 +0000750 if (access(path.c_str(), R_OK) == 0)
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800751 mHealthdConfig->batteryTechnologyPath = path;
752 }
753
754 break;
755
756 case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
757 break;
758 }
759
760 // Look for "is_dock" file
761 path.clear();
762 path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH, name);
Tomasz Wasilczyk18b74612023-08-10 23:29:50 +0000763 if (access(path.c_str(), R_OK) == 0) {
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800764 path.clear();
765 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
Tomasz Wasilczyk18b74612023-08-10 23:29:50 +0000766 if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800767 }
768 }
769 }
770
771 // Typically the case for devices which do not have a battery and
772 // and are always plugged into AC mains.
773 if (!mBatteryDevicePresent) {
774 KLOG_WARNING(LOG_TAG, "No battery devices found\n");
775 hc->periodic_chores_interval_fast = -1;
776 hc->periodic_chores_interval_slow = -1;
777 } else {
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000778 if (mHealthdConfig->batteryStatusPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800779 KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000780 if (mHealthdConfig->batteryHealthPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800781 KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000782 if (mHealthdConfig->batteryPresentPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800783 KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000784 if (mHealthdConfig->batteryCapacityPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800785 KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000786 if (mHealthdConfig->batteryVoltagePath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800787 KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000788 if (mHealthdConfig->batteryTemperaturePath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800789 KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000790 if (mHealthdConfig->batteryTechnologyPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800791 KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000792 if (mHealthdConfig->batteryCurrentNowPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800793 KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000794 if (mHealthdConfig->batteryFullChargePath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800795 KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000796 if (mHealthdConfig->batteryCycleCountPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800797 KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000798 if (mHealthdConfig->batteryCapacityLevelPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800799 KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000800 if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800801 KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
Tomasz Wasilczykf5971292023-08-14 18:18:26 +0000802 if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
Jack Wu2ed5c9c2023-01-21 14:33:55 +0800803 KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
804 }
805
806 if (property_get("ro.boot.fake_battery", pval, NULL) > 0
807 && strtol(pval, NULL, 10) != 0) {
808 mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;
809 mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
810 }
811}
812
813}; // namespace android