blob: b85f23f6f8c2459030b731c4d8cfca6eacc6518f [file] [log] [blame]
Todd Poynor752faf22013-06-12 13:25:59 -07001/*
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
Yabin Cuie98e1772016-02-17 12:21:34 -080019#include <healthd/healthd.h>
20#include <healthd/BatteryMonitor.h>
Todd Poynor752faf22013-06-12 13:25:59 -070021
22#include <dirent.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <stdio.h>
26#include <stdlib.h>
Mark Salyzynacb1ddf2015-07-23 09:22:50 -070027#include <sys/types.h>
Todd Poynor752faf22013-06-12 13:25:59 -070028#include <unistd.h>
Thierry Strudelf73de6f2019-01-11 17:09:20 -080029
30#include <algorithm>
James Hawkins588a2ca2016-02-18 14:52:46 -080031#include <memory>
Yifan Hong1d4368b2019-10-07 11:18:04 -070032#include <optional>
Mark Salyzynacb1ddf2015-07-23 09:22:50 -070033
Michael Scott3217c5c2016-06-05 11:20:13 -070034#include <android-base/file.h>
Elliott Hughesda46b392016-10-11 17:09:00 -070035#include <android-base/parseint.h>
Michael Scott3217c5c2016-06-05 11:20:13 -070036#include <android-base/strings.h>
Yifan Hong1d4368b2019-10-07 11:18:04 -070037#include <android/hardware/health/2.1/types.h>
Todd Poynor752faf22013-06-12 13:25:59 -070038#include <batteryservice/BatteryService.h>
39#include <cutils/klog.h>
Todd Poynor3db03a52014-05-21 16:28:13 -070040#include <cutils/properties.h>
Todd Poynorc133b712013-08-14 17:39:13 -070041#include <utils/Errors.h>
Todd Poynor752faf22013-06-12 13:25:59 -070042#include <utils/String8.h>
43#include <utils/Vector.h>
44
45#define POWER_SUPPLY_SUBSYSTEM "power_supply"
46#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
Ruchi Kandoia78fc232014-07-10 15:06:21 -070047#define FAKE_BATTERY_CAPACITY 42
48#define FAKE_BATTERY_TEMPERATURE 424
Ruchi Kandoi5c09ec12016-02-25 16:19:30 -080049#define MILLION 1.0e6
Badhri Jagan Sridharan40e1df42015-10-27 10:43:53 -070050#define DEFAULT_VBUS_VOLTAGE 5000000
Todd Poynor752faf22013-06-12 13:25:59 -070051
Yifan Hong1d4368b2019-10-07 11:18:04 -070052using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
53using HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;
54using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
55using android::hardware::health::V1_0::BatteryHealth;
56using android::hardware::health::V1_0::BatteryStatus;
Yifan Hong35cb0832019-10-07 13:58:29 -070057using android::hardware::health::V2_1::BatteryCapacityLevel;
Yifan Hong1d4368b2019-10-07 11:18:04 -070058
Todd Poynor752faf22013-06-12 13:25:59 -070059namespace android {
60
Yifan Hong1d4368b2019-10-07 11:18:04 -070061template <typename T>
62struct SysfsStringEnumMap {
Mark Salyzyn6f5b47f2014-05-15 15:00:59 -070063 const char* s;
Yifan Hong1d4368b2019-10-07 11:18:04 -070064 T val;
Todd Poynor752faf22013-06-12 13:25:59 -070065};
66
Yifan Hong1d4368b2019-10-07 11:18:04 -070067template <typename T>
68static std::optional<T> mapSysfsString(const char* str, SysfsStringEnumMap<T> map[]) {
Todd Poynor752faf22013-06-12 13:25:59 -070069 for (int i = 0; map[i].s; i++)
70 if (!strcmp(str, map[i].s))
71 return map[i].val;
72
Yifan Hong1d4368b2019-10-07 11:18:04 -070073 return std::nullopt;
Yabin Cuidb04a492016-02-16 17:19:23 -080074}
75
Yifan Hong6cabe9b2019-11-05 17:04:50 -080076static void initHealthInfo(HealthInfo_2_1* health_info_2_1) {
77 *health_info_2_1 = HealthInfo_2_1{};
78
79 // HIDL enum values are zero initialized, so they need to be initialized
80 // properly.
81 health_info_2_1->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
82 auto* props = &health_info_2_1->legacy.legacy;
83 props->batteryStatus = BatteryStatus::UNKNOWN;
84 props->batteryHealth = BatteryHealth::UNKNOWN;
85}
86
Todd Poynore030a102018-01-19 14:03:59 -080087BatteryMonitor::BatteryMonitor()
88 : mHealthdConfig(nullptr),
89 mBatteryDevicePresent(false),
90 mBatteryFixedCapacity(0),
Yifan Hong1d4368b2019-10-07 11:18:04 -070091 mBatteryFixedTemperature(0),
Yifan Hong6cabe9b2019-11-05 17:04:50 -080092 mHealthInfo(std::make_unique<HealthInfo_2_1>()) {
93 initHealthInfo(mHealthInfo.get());
94}
Yifan Hong1d4368b2019-10-07 11:18:04 -070095
96BatteryMonitor::~BatteryMonitor() {}
97
98const HealthInfo_1_0& BatteryMonitor::getHealthInfo_1_0() const {
99 return getHealthInfo_2_0().legacy;
Yabin Cuidb04a492016-02-16 17:19:23 -0800100}
101
Yifan Hong1d4368b2019-10-07 11:18:04 -0700102const HealthInfo_2_0& BatteryMonitor::getHealthInfo_2_0() const {
103 return getHealthInfo_2_1().legacy;
Hridya Valsaraju7fa72252018-01-12 17:44:33 -0800104}
105
Yifan Hong1d4368b2019-10-07 11:18:04 -0700106const HealthInfo_2_1& BatteryMonitor::getHealthInfo_2_1() const {
107 return *mHealthInfo;
108}
109
110BatteryStatus getBatteryStatus(const char* status) {
111 static SysfsStringEnumMap<BatteryStatus> batteryStatusMap[] = {
112 {"Unknown", BatteryStatus::UNKNOWN},
113 {"Charging", BatteryStatus::CHARGING},
114 {"Discharging", BatteryStatus::DISCHARGING},
115 {"Not charging", BatteryStatus::NOT_CHARGING},
116 {"Full", BatteryStatus::FULL},
117 {NULL, BatteryStatus::UNKNOWN},
Todd Poynor752faf22013-06-12 13:25:59 -0700118 };
119
Yifan Hong1d4368b2019-10-07 11:18:04 -0700120 auto ret = mapSysfsString(status, batteryStatusMap);
121 if (!ret) {
Todd Poynor752faf22013-06-12 13:25:59 -0700122 KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status);
Yifan Hong1d4368b2019-10-07 11:18:04 -0700123 *ret = BatteryStatus::UNKNOWN;
Todd Poynor752faf22013-06-12 13:25:59 -0700124 }
125
Yifan Hong1d4368b2019-10-07 11:18:04 -0700126 return *ret;
Todd Poynor752faf22013-06-12 13:25:59 -0700127}
128
Stephane Lee86f9f6a2019-12-19 15:09:41 -0800129BatteryCapacityLevel getBatteryCapacityLevel(const char* capacityLevel) {
130 static SysfsStringEnumMap<BatteryCapacityLevel> batteryCapacityLevelMap[] = {
131 {"Unknown", BatteryCapacityLevel::UNKNOWN},
132 {"Critical", BatteryCapacityLevel::CRITICAL},
133 {"Low", BatteryCapacityLevel::LOW},
134 {"Normal", BatteryCapacityLevel::NORMAL},
135 {"High", BatteryCapacityLevel::HIGH},
136 {"Full", BatteryCapacityLevel::FULL},
137 {NULL, BatteryCapacityLevel::UNKNOWN},
138 };
139
140 auto ret = mapSysfsString(capacityLevel, batteryCapacityLevelMap);
141 if (!ret) {
142 KLOG_WARNING(LOG_TAG, "Unknown battery capacity level '%s'\n", capacityLevel);
143 *ret = BatteryCapacityLevel::UNKNOWN;
144 }
145
146 return *ret;
147}
148
Yifan Hong1d4368b2019-10-07 11:18:04 -0700149BatteryHealth getBatteryHealth(const char* status) {
150 static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {
151 {"Unknown", BatteryHealth::UNKNOWN},
152 {"Good", BatteryHealth::GOOD},
153 {"Overheat", BatteryHealth::OVERHEAT},
154 {"Dead", BatteryHealth::DEAD},
155 {"Over voltage", BatteryHealth::OVER_VOLTAGE},
156 {"Unspecified failure", BatteryHealth::UNSPECIFIED_FAILURE},
157 {"Cold", BatteryHealth::COLD},
158 // battery health values from JEITA spec
159 {"Warm", BatteryHealth::GOOD},
160 {"Cool", BatteryHealth::GOOD},
161 {"Hot", BatteryHealth::OVERHEAT},
162 {NULL, BatteryHealth::UNKNOWN},
Todd Poynor752faf22013-06-12 13:25:59 -0700163 };
164
Yifan Hong1d4368b2019-10-07 11:18:04 -0700165 auto ret = mapSysfsString(status, batteryHealthMap);
166 if (!ret) {
Todd Poynor752faf22013-06-12 13:25:59 -0700167 KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status);
Yifan Hong1d4368b2019-10-07 11:18:04 -0700168 *ret = BatteryHealth::UNKNOWN;
Todd Poynor752faf22013-06-12 13:25:59 -0700169 }
170
Yifan Hong1d4368b2019-10-07 11:18:04 -0700171 return *ret;
Todd Poynor752faf22013-06-12 13:25:59 -0700172}
173
Michael Scott3217c5c2016-06-05 11:20:13 -0700174int BatteryMonitor::readFromFile(const String8& path, std::string* buf) {
Steven Moreland2aac3352017-03-10 22:31:08 -0800175 if (android::base::ReadFileToString(path.c_str(), buf)) {
Michael Scott3217c5c2016-06-05 11:20:13 -0700176 *buf = android::base::Trim(*buf);
Todd Poynor752faf22013-06-12 13:25:59 -0700177 }
Michael Scott3217c5c2016-06-05 11:20:13 -0700178 return buf->length();
Todd Poynor752faf22013-06-12 13:25:59 -0700179}
180
181BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
Yifan Hong1d4368b2019-10-07 11:18:04 -0700182 static SysfsStringEnumMap<int> supplyTypeMap[] = {
183 {"Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
184 {"Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY},
185 {"UPS", ANDROID_POWER_SUPPLY_TYPE_AC},
186 {"Mains", ANDROID_POWER_SUPPLY_TYPE_AC},
187 {"USB", ANDROID_POWER_SUPPLY_TYPE_USB},
188 {"USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC},
189 {"USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC},
190 {"USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC},
191 {"USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC},
192 {"USB_C", ANDROID_POWER_SUPPLY_TYPE_AC},
193 {"USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC},
194 {"USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB},
195 {"Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
196 {NULL, 0},
Todd Poynor752faf22013-06-12 13:25:59 -0700197 };
Yifan Hong1d4368b2019-10-07 11:18:04 -0700198 std::string buf;
Todd Poynor752faf22013-06-12 13:25:59 -0700199
Michael Scott3217c5c2016-06-05 11:20:13 -0700200 if (readFromFile(path, &buf) <= 0)
Todd Poynor752faf22013-06-12 13:25:59 -0700201 return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
202
Yifan Hong1d4368b2019-10-07 11:18:04 -0700203 auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);
John Stultz47a6bf02019-11-06 00:23:34 +0000204 if (!ret) {
Michael Scott3217c5c2016-06-05 11:20:13 -0700205 KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
Yifan Hong1d4368b2019-10-07 11:18:04 -0700206 *ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
Johan Redestig32828612016-02-03 13:45:54 +0100207 }
Todd Poynor752faf22013-06-12 13:25:59 -0700208
Yifan Hong1d4368b2019-10-07 11:18:04 -0700209 return static_cast<BatteryMonitor::PowerSupplyType>(*ret);
Todd Poynor752faf22013-06-12 13:25:59 -0700210}
211
212bool BatteryMonitor::getBooleanField(const String8& path) {
Michael Scott3217c5c2016-06-05 11:20:13 -0700213 std::string buf;
Todd Poynor752faf22013-06-12 13:25:59 -0700214 bool value = false;
Michael Scott3217c5c2016-06-05 11:20:13 -0700215
216 if (readFromFile(path, &buf) > 0)
217 if (buf[0] != '0')
Todd Poynor752faf22013-06-12 13:25:59 -0700218 value = true;
Todd Poynor752faf22013-06-12 13:25:59 -0700219
220 return value;
221}
222
223int BatteryMonitor::getIntField(const String8& path) {
Michael Scott3217c5c2016-06-05 11:20:13 -0700224 std::string buf;
Todd Poynor752faf22013-06-12 13:25:59 -0700225 int value = 0;
Michael Scott3217c5c2016-06-05 11:20:13 -0700226
227 if (readFromFile(path, &buf) > 0)
Elliott Hughesda46b392016-10-11 17:09:00 -0700228 android::base::ParseInt(buf, &value);
Michael Scott3217c5c2016-06-05 11:20:13 -0700229
Todd Poynor752faf22013-06-12 13:25:59 -0700230 return value;
231}
232
Yifan Hong1353e702019-10-07 10:41:30 -0700233void BatteryMonitor::updateValues(void) {
Yifan Hong6cabe9b2019-11-05 17:04:50 -0800234 initHealthInfo(mHealthInfo.get());
Yifan Hong1d4368b2019-10-07 11:18:04 -0700235
236 HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
Todd Poynor752faf22013-06-12 13:25:59 -0700237
Todd Poynorf5d30122013-08-12 17:03:35 -0700238 if (!mHealthdConfig->batteryPresentPath.isEmpty())
239 props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
Todd Poynor752faf22013-06-12 13:25:59 -0700240 else
Todd Poynor6dcc45e2013-10-21 20:26:25 -0700241 props.batteryPresent = mBatteryDevicePresent;
Todd Poynor752faf22013-06-12 13:25:59 -0700242
Todd Poynor3db03a52014-05-21 16:28:13 -0700243 props.batteryLevel = mBatteryFixedCapacity ?
244 mBatteryFixedCapacity :
245 getIntField(mHealthdConfig->batteryCapacityPath);
Todd Poynorf5d30122013-08-12 17:03:35 -0700246 props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
Todd Poynorb45f1f52013-07-30 18:57:16 -0700247
Ruchi Kandoicc338802015-08-24 13:01:16 -0700248 if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
249 props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath) / 1000;
250
251 if (!mHealthdConfig->batteryFullChargePath.isEmpty())
252 props.batteryFullCharge = getIntField(mHealthdConfig->batteryFullChargePath);
253
254 if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
255 props.batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
256
Ruchi Kandoi3f9886b2016-04-07 12:34:40 -0700257 if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
258 props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
259
Yifan Hong35cb0832019-10-07 13:58:29 -0700260 if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
261 mHealthInfo->legacy.batteryCurrentAverage =
262 getIntField(mHealthdConfig->batteryCurrentAvgPath);
263
Stephane Lee86f9f6a2019-12-19 15:09:41 -0800264 if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
265 mHealthInfo->batteryChargeTimeToFullNowSeconds =
266 getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
267
Stephane Lee1c108ed2020-02-10 18:23:57 -0800268 if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
269 mHealthInfo->batteryFullChargeDesignCapacityUah =
270 getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
Yifan Hong35cb0832019-10-07 13:58:29 -0700271
Todd Poynor3db03a52014-05-21 16:28:13 -0700272 props.batteryTemperature = mBatteryFixedTemperature ?
273 mBatteryFixedTemperature :
274 getIntField(mHealthdConfig->batteryTemperaturePath);
Todd Poynor752faf22013-06-12 13:25:59 -0700275
Michael Scott3217c5c2016-06-05 11:20:13 -0700276 std::string buf;
Todd Poynor752faf22013-06-12 13:25:59 -0700277
Stephane Lee86f9f6a2019-12-19 15:09:41 -0800278 if (readFromFile(mHealthdConfig->batteryCapacityLevelPath, &buf) > 0)
279 mHealthInfo->batteryCapacityLevel = getBatteryCapacityLevel(buf.c_str());
280
Michael Scott3217c5c2016-06-05 11:20:13 -0700281 if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
282 props.batteryStatus = getBatteryStatus(buf.c_str());
Todd Poynor752faf22013-06-12 13:25:59 -0700283
Michael Scott3217c5c2016-06-05 11:20:13 -0700284 if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
285 props.batteryHealth = getBatteryHealth(buf.c_str());
Todd Poynor752faf22013-06-12 13:25:59 -0700286
Michael Scott3217c5c2016-06-05 11:20:13 -0700287 if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
288 props.batteryTechnology = String8(buf.c_str());
Todd Poynor752faf22013-06-12 13:25:59 -0700289
Badhri Jagan Sridharan40e1df42015-10-27 10:43:53 -0700290 double MaxPower = 0;
Todd Poynor752faf22013-06-12 13:25:59 -0700291
ShevT9d98a6a2018-07-26 11:47:47 +0300292 for (size_t i = 0; i < mChargerNames.size(); i++) {
Todd Poynor752faf22013-06-12 13:25:59 -0700293 String8 path;
294 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
295 mChargerNames[i].string());
Michael Scott3217c5c2016-06-05 11:20:13 -0700296 if (getIntField(path)) {
297 path.clear();
298 path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
299 mChargerNames[i].string());
300 switch(readPowerSupplyType(path)) {
301 case ANDROID_POWER_SUPPLY_TYPE_AC:
302 props.chargerAcOnline = true;
303 break;
304 case ANDROID_POWER_SUPPLY_TYPE_USB:
305 props.chargerUsbOnline = true;
306 break;
307 case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
308 props.chargerWirelessOnline = true;
309 break;
310 default:
311 KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
312 mChargerNames[i].string());
313 }
314 path.clear();
315 path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
316 mChargerNames[i].string());
Dmitry Shmidt9f6b80c2016-06-20 12:58:37 -0700317 int ChargingCurrent =
Badhri Jagan Sridharan40e1df42015-10-27 10:43:53 -0700318 (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
319
Dmitry Shmidt9f6b80c2016-06-20 12:58:37 -0700320 path.clear();
321 path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
322 mChargerNames[i].string());
Badhri Jagan Sridharan40e1df42015-10-27 10:43:53 -0700323
Dmitry Shmidt9f6b80c2016-06-20 12:58:37 -0700324 int ChargingVoltage =
325 (access(path.string(), R_OK) == 0) ? getIntField(path) :
326 DEFAULT_VBUS_VOLTAGE;
Badhri Jagan Sridharan40e1df42015-10-27 10:43:53 -0700327
Dmitry Shmidt9f6b80c2016-06-20 12:58:37 -0700328 double power = ((double)ChargingCurrent / MILLION) *
329 ((double)ChargingVoltage / MILLION);
330 if (MaxPower < power) {
331 props.maxChargingCurrent = ChargingCurrent;
332 props.maxChargingVoltage = ChargingVoltage;
333 MaxPower = power;
Todd Poynor752faf22013-06-12 13:25:59 -0700334 }
335 }
336 }
Yifan Hong1353e702019-10-07 10:41:30 -0700337}
Todd Poynor752faf22013-06-12 13:25:59 -0700338
Yifan Hong1353e702019-10-07 10:41:30 -0700339void BatteryMonitor::logValues(void) {
340 char dmesgline[256];
341 size_t len;
Yifan Hong1d4368b2019-10-07 11:18:04 -0700342 const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
Yifan Hong1353e702019-10-07 10:41:30 -0700343 if (props.batteryPresent) {
344 snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
345 props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "",
346 abs(props.batteryTemperature / 10), abs(props.batteryTemperature % 10),
347 props.batteryHealth, props.batteryStatus);
Todd Poynorb45f1f52013-07-30 18:57:16 -0700348
Yifan Hong1353e702019-10-07 10:41:30 -0700349 len = strlen(dmesgline);
350 if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
351 len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
352 props.batteryCurrent);
Todd Poynor10b235e2013-08-07 15:25:14 -0700353 }
354
Yifan Hong1353e702019-10-07 10:41:30 -0700355 if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
356 len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
357 props.batteryFullCharge);
358 }
Mark Salyzynacb1ddf2015-07-23 09:22:50 -0700359
Yifan Hong1353e702019-10-07 10:41:30 -0700360 if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
361 len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
362 props.batteryCycleCount);
363 }
364 } else {
365 len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
Todd Poynorb45f1f52013-07-30 18:57:16 -0700366 }
367
Yifan Hong1353e702019-10-07 10:41:30 -0700368 snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
369 props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "",
370 props.chargerWirelessOnline ? "w" : "");
371
372 KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
373}
374
375bool BatteryMonitor::isChargerOnline() {
Yifan Hong1d4368b2019-10-07 11:18:04 -0700376 const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
Todd Poynor752faf22013-06-12 13:25:59 -0700377 return props.chargerAcOnline | props.chargerUsbOnline |
378 props.chargerWirelessOnline;
379}
380
Yabin Cuiaedf6032016-02-19 18:03:23 -0800381int BatteryMonitor::getChargeStatus() {
Yifan Hong1d4368b2019-10-07 11:18:04 -0700382 BatteryStatus result = BatteryStatus::UNKNOWN;
Yabin Cuiaedf6032016-02-19 18:03:23 -0800383 if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
Michael Scott3217c5c2016-06-05 11:20:13 -0700384 std::string buf;
385 if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
386 result = getBatteryStatus(buf.c_str());
Yabin Cuiaedf6032016-02-19 18:03:23 -0800387 }
Yifan Hong1d4368b2019-10-07 11:18:04 -0700388 return static_cast<int>(result);
Yabin Cuiaedf6032016-02-19 18:03:23 -0800389}
390
Todd Poynorc133b712013-08-14 17:39:13 -0700391status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
392 status_t ret = BAD_VALUE;
Jin Qian72adf112017-02-02 17:31:13 -0800393 std::string buf;
Todd Poynorc133b712013-08-14 17:39:13 -0700394
Todd Poynor8f132af2014-05-08 17:15:45 -0700395 val->valueInt64 = LONG_MIN;
396
Todd Poynorc133b712013-08-14 17:39:13 -0700397 switch(id) {
398 case BATTERY_PROP_CHARGE_COUNTER:
399 if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
Todd Poynor8f132af2014-05-08 17:15:45 -0700400 val->valueInt64 =
Todd Poynorc133b712013-08-14 17:39:13 -0700401 getIntField(mHealthdConfig->batteryChargeCounterPath);
Elliott Hughes643268f2018-10-08 11:10:11 -0700402 ret = OK;
Todd Poynorc133b712013-08-14 17:39:13 -0700403 } else {
404 ret = NAME_NOT_FOUND;
405 }
406 break;
407
408 case BATTERY_PROP_CURRENT_NOW:
409 if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
Todd Poynor8f132af2014-05-08 17:15:45 -0700410 val->valueInt64 =
Todd Poynorc133b712013-08-14 17:39:13 -0700411 getIntField(mHealthdConfig->batteryCurrentNowPath);
Elliott Hughes643268f2018-10-08 11:10:11 -0700412 ret = OK;
Todd Poynorc133b712013-08-14 17:39:13 -0700413 } else {
414 ret = NAME_NOT_FOUND;
415 }
416 break;
417
Todd Poynorbc102112013-08-27 18:11:49 -0700418 case BATTERY_PROP_CURRENT_AVG:
419 if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
Todd Poynor8f132af2014-05-08 17:15:45 -0700420 val->valueInt64 =
Todd Poynorbc102112013-08-27 18:11:49 -0700421 getIntField(mHealthdConfig->batteryCurrentAvgPath);
Elliott Hughes643268f2018-10-08 11:10:11 -0700422 ret = OK;
Todd Poynorbc102112013-08-27 18:11:49 -0700423 } else {
424 ret = NAME_NOT_FOUND;
425 }
426 break;
427
Paul Lawrence347c8de2014-03-19 15:04:40 -0700428 case BATTERY_PROP_CAPACITY:
429 if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
Todd Poynor8f132af2014-05-08 17:15:45 -0700430 val->valueInt64 =
Paul Lawrence347c8de2014-03-19 15:04:40 -0700431 getIntField(mHealthdConfig->batteryCapacityPath);
Elliott Hughes643268f2018-10-08 11:10:11 -0700432 ret = OK;
Paul Lawrence347c8de2014-03-19 15:04:40 -0700433 } else {
434 ret = NAME_NOT_FOUND;
435 }
436 break;
437
Todd Poynor8f132af2014-05-08 17:15:45 -0700438 case BATTERY_PROP_ENERGY_COUNTER:
Todd Poynore14b37e2014-05-20 13:54:40 -0700439 if (mHealthdConfig->energyCounter) {
440 ret = mHealthdConfig->energyCounter(&val->valueInt64);
441 } else {
442 ret = NAME_NOT_FOUND;
443 }
Todd Poynor8f132af2014-05-08 17:15:45 -0700444 break;
445
Jin Qian72adf112017-02-02 17:31:13 -0800446 case BATTERY_PROP_BATTERY_STATUS:
Todd Poynore030a102018-01-19 14:03:59 -0800447 val->valueInt64 = getChargeStatus();
Elliott Hughes643268f2018-10-08 11:10:11 -0700448 ret = OK;
Jin Qian72adf112017-02-02 17:31:13 -0800449 break;
450
Todd Poynorc133b712013-08-14 17:39:13 -0700451 default:
452 break;
453 }
454
Todd Poynorc133b712013-08-14 17:39:13 -0700455 return ret;
456}
457
Todd Poynor020369d2013-09-18 20:09:33 -0700458void BatteryMonitor::dumpState(int fd) {
459 int v;
460 char vs[128];
Yifan Hong1d4368b2019-10-07 11:18:04 -0700461 const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
Todd Poynor020369d2013-09-18 20:09:33 -0700462
Badhri Jagan Sridharan40e1df42015-10-27 10:43:53 -0700463 snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
Todd Poynor020369d2013-09-18 20:09:33 -0700464 props.chargerAcOnline, props.chargerUsbOnline,
Badhri Jagan Sridharan40e1df42015-10-27 10:43:53 -0700465 props.chargerWirelessOnline, props.maxChargingCurrent,
466 props.maxChargingVoltage);
Todd Poynor020369d2013-09-18 20:09:33 -0700467 write(fd, vs, strlen(vs));
468 snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
469 props.batteryStatus, props.batteryHealth, props.batteryPresent);
470 write(fd, vs, strlen(vs));
471 snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n",
472 props.batteryLevel, props.batteryVoltage,
473 props.batteryTemperature);
474 write(fd, vs, strlen(vs));
475
476 if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
477 v = getIntField(mHealthdConfig->batteryCurrentNowPath);
478 snprintf(vs, sizeof(vs), "current now: %d\n", v);
479 write(fd, vs, strlen(vs));
480 }
481
482 if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
483 v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
484 snprintf(vs, sizeof(vs), "current avg: %d\n", v);
485 write(fd, vs, strlen(vs));
486 }
487
488 if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
489 v = getIntField(mHealthdConfig->batteryChargeCounterPath);
490 snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
491 write(fd, vs, strlen(vs));
492 }
Ruchi Kandoicc338802015-08-24 13:01:16 -0700493
494 if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
495 snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrent);
496 write(fd, vs, strlen(vs));
497 }
498
499 if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
500 snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
501 write(fd, vs, strlen(vs));
502 }
503
504 if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
505 snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullCharge);
506 write(fd, vs, strlen(vs));
507 }
Todd Poynor020369d2013-09-18 20:09:33 -0700508}
509
Todd Poynorc7464c92013-09-10 12:40:00 -0700510void BatteryMonitor::init(struct healthd_config *hc) {
Todd Poynor752faf22013-06-12 13:25:59 -0700511 String8 path;
Todd Poynor3db03a52014-05-21 16:28:13 -0700512 char pval[PROPERTY_VALUE_MAX];
Todd Poynor752faf22013-06-12 13:25:59 -0700513
Todd Poynorf5d30122013-08-12 17:03:35 -0700514 mHealthdConfig = hc;
James Hawkins588a2ca2016-02-18 14:52:46 -0800515 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);
Todd Poynor752faf22013-06-12 13:25:59 -0700516 if (dir == NULL) {
517 KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
518 } else {
519 struct dirent* entry;
520
James Hawkins588a2ca2016-02-18 14:52:46 -0800521 while ((entry = readdir(dir.get()))) {
Todd Poynor752faf22013-06-12 13:25:59 -0700522 const char* name = entry->d_name;
Thierry Strudelf73de6f2019-01-11 17:09:20 -0800523 std::vector<String8>::iterator itIgnoreName;
Todd Poynor752faf22013-06-12 13:25:59 -0700524
525 if (!strcmp(name, ".") || !strcmp(name, ".."))
526 continue;
527
Thierry Strudelf73de6f2019-01-11 17:09:20 -0800528 itIgnoreName = find(hc->ignorePowerSupplyNames.begin(),
529 hc->ignorePowerSupplyNames.end(), String8(name));
530 if (itIgnoreName != hc->ignorePowerSupplyNames.end())
531 continue;
532
Todd Poynor752faf22013-06-12 13:25:59 -0700533 // Look for "type" file in each subdirectory
534 path.clear();
535 path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
536 switch(readPowerSupplyType(path)) {
537 case ANDROID_POWER_SUPPLY_TYPE_AC:
538 case ANDROID_POWER_SUPPLY_TYPE_USB:
539 case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
540 path.clear();
541 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
542 if (access(path.string(), R_OK) == 0)
543 mChargerNames.add(String8(name));
544 break;
545
546 case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
Todd Poynor6dcc45e2013-10-21 20:26:25 -0700547 mBatteryDevicePresent = true;
548
Todd Poynorf5d30122013-08-12 17:03:35 -0700549 if (mHealthdConfig->batteryStatusPath.isEmpty()) {
Todd Poynor752faf22013-06-12 13:25:59 -0700550 path.clear();
Todd Poynorf5d30122013-08-12 17:03:35 -0700551 path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
552 name);
Todd Poynor752faf22013-06-12 13:25:59 -0700553 if (access(path, R_OK) == 0)
Todd Poynorf5d30122013-08-12 17:03:35 -0700554 mHealthdConfig->batteryStatusPath = path;
Todd Poynor752faf22013-06-12 13:25:59 -0700555 }
556
Todd Poynorf5d30122013-08-12 17:03:35 -0700557 if (mHealthdConfig->batteryHealthPath.isEmpty()) {
Todd Poynor752faf22013-06-12 13:25:59 -0700558 path.clear();
Todd Poynorf5d30122013-08-12 17:03:35 -0700559 path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
560 name);
Todd Poynor752faf22013-06-12 13:25:59 -0700561 if (access(path, R_OK) == 0)
Todd Poynorf5d30122013-08-12 17:03:35 -0700562 mHealthdConfig->batteryHealthPath = path;
Todd Poynor752faf22013-06-12 13:25:59 -0700563 }
564
Todd Poynorf5d30122013-08-12 17:03:35 -0700565 if (mHealthdConfig->batteryPresentPath.isEmpty()) {
566 path.clear();
567 path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
568 name);
569 if (access(path, R_OK) == 0)
570 mHealthdConfig->batteryPresentPath = path;
571 }
572
573 if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
574 path.clear();
575 path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
576 name);
577 if (access(path, R_OK) == 0)
578 mHealthdConfig->batteryCapacityPath = path;
579 }
580
581 if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
582 path.clear();
583 path.appendFormat("%s/%s/voltage_now",
584 POWER_SUPPLY_SYSFS_PATH, name);
585 if (access(path, R_OK) == 0) {
586 mHealthdConfig->batteryVoltagePath = path;
Todd Poynorf5d30122013-08-12 17:03:35 -0700587 }
588 }
589
Ruchi Kandoicc338802015-08-24 13:01:16 -0700590 if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
591 path.clear();
592 path.appendFormat("%s/%s/charge_full",
593 POWER_SUPPLY_SYSFS_PATH, name);
594 if (access(path, R_OK) == 0)
595 mHealthdConfig->batteryFullChargePath = path;
596 }
597
Todd Poynorf5d30122013-08-12 17:03:35 -0700598 if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
599 path.clear();
600 path.appendFormat("%s/%s/current_now",
601 POWER_SUPPLY_SYSFS_PATH, name);
602 if (access(path, R_OK) == 0)
603 mHealthdConfig->batteryCurrentNowPath = path;
604 }
605
Ruchi Kandoicc338802015-08-24 13:01:16 -0700606 if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
607 path.clear();
608 path.appendFormat("%s/%s/cycle_count",
609 POWER_SUPPLY_SYSFS_PATH, name);
610 if (access(path, R_OK) == 0)
611 mHealthdConfig->batteryCycleCountPath = path;
612 }
613
Stephane Lee86f9f6a2019-12-19 15:09:41 -0800614 if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
615 path.clear();
616 path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
617 if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
618 }
619
620 if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
621 path.clear();
622 path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
623 if (access(path, R_OK) == 0)
624 mHealthdConfig->batteryChargeTimeToFullNowPath = path;
625 }
626
Stephane Lee1c108ed2020-02-10 18:23:57 -0800627 if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty()) {
628 path.clear();
629 path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
630 if (access(path, R_OK) == 0)
631 mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
632 }
633
Todd Poynorbc102112013-08-27 18:11:49 -0700634 if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
635 path.clear();
636 path.appendFormat("%s/%s/current_avg",
637 POWER_SUPPLY_SYSFS_PATH, name);
638 if (access(path, R_OK) == 0)
639 mHealthdConfig->batteryCurrentAvgPath = path;
640 }
641
Todd Poynorf5d30122013-08-12 17:03:35 -0700642 if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
643 path.clear();
644 path.appendFormat("%s/%s/charge_counter",
645 POWER_SUPPLY_SYSFS_PATH, name);
646 if (access(path, R_OK) == 0)
647 mHealthdConfig->batteryChargeCounterPath = path;
648 }
649
650 if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
651 path.clear();
652 path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
653 name);
654 if (access(path, R_OK) == 0) {
655 mHealthdConfig->batteryTemperaturePath = path;
Todd Poynorf5d30122013-08-12 17:03:35 -0700656 }
657 }
658
659 if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
660 path.clear();
661 path.appendFormat("%s/%s/technology",
662 POWER_SUPPLY_SYSFS_PATH, name);
663 if (access(path, R_OK) == 0)
664 mHealthdConfig->batteryTechnologyPath = path;
665 }
666
Todd Poynor752faf22013-06-12 13:25:59 -0700667 break;
668
669 case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
670 break;
671 }
672 }
Todd Poynor752faf22013-06-12 13:25:59 -0700673 }
674
Ian Pedowitz585ab652015-10-12 19:01:00 -0700675 // Typically the case for devices which do not have a battery and
676 // and are always plugged into AC mains.
Todd Poynor6dcc45e2013-10-21 20:26:25 -0700677 if (!mBatteryDevicePresent) {
Todd Poynorebeb0c02014-09-23 14:54:24 -0700678 KLOG_WARNING(LOG_TAG, "No battery devices found\n");
Todd Poynor6dcc45e2013-10-21 20:26:25 -0700679 hc->periodic_chores_interval_fast = -1;
680 hc->periodic_chores_interval_slow = -1;
681 } else {
682 if (mHealthdConfig->batteryStatusPath.isEmpty())
683 KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
684 if (mHealthdConfig->batteryHealthPath.isEmpty())
685 KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
686 if (mHealthdConfig->batteryPresentPath.isEmpty())
687 KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
688 if (mHealthdConfig->batteryCapacityPath.isEmpty())
689 KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
690 if (mHealthdConfig->batteryVoltagePath.isEmpty())
691 KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
692 if (mHealthdConfig->batteryTemperaturePath.isEmpty())
693 KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
694 if (mHealthdConfig->batteryTechnologyPath.isEmpty())
695 KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
Ruchi Kandoif18ec9f2015-09-28 13:35:59 -0700696 if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
Ruchi Kandoicc338802015-08-24 13:01:16 -0700697 KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
698 if (mHealthdConfig->batteryFullChargePath.isEmpty())
699 KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
700 if (mHealthdConfig->batteryCycleCountPath.isEmpty())
701 KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
Stephane Lee86f9f6a2019-12-19 15:09:41 -0800702 if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
703 KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
704 if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
705 KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
Stephane Lee1c108ed2020-02-10 18:23:57 -0800706 if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
707 KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
Todd Poynor6dcc45e2013-10-21 20:26:25 -0700708 }
Todd Poynor3db03a52014-05-21 16:28:13 -0700709
Ruchi Kandoia78fc232014-07-10 15:06:21 -0700710 if (property_get("ro.boot.fake_battery", pval, NULL) > 0
711 && strtol(pval, NULL, 10) != 0) {
712 mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;
713 mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
714 }
Todd Poynor752faf22013-06-12 13:25:59 -0700715}
716
717}; // namespace android