blob: 110ed038f660ed43813b1517aa3ba73fd85c0dbd [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 Cuif6dbc6d2016-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>
Mark Salyzynacb1ddf2015-07-23 09:22:50 -070029
Todd Poynor752faf22013-06-12 13:25:59 -070030#include <batteryservice/BatteryService.h>
31#include <cutils/klog.h>
Todd Poynor3db03a52014-05-21 16:28:13 -070032#include <cutils/properties.h>
Todd Poynorc133b712013-08-14 17:39:13 -070033#include <utils/Errors.h>
Todd Poynor752faf22013-06-12 13:25:59 -070034#include <utils/String8.h>
35#include <utils/Vector.h>
36
37#define POWER_SUPPLY_SUBSYSTEM "power_supply"
38#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
Ruchi Kandoia78fc232014-07-10 15:06:21 -070039#define FAKE_BATTERY_CAPACITY 42
40#define FAKE_BATTERY_TEMPERATURE 424
Ruchi Kandoif18ec9f2015-09-28 13:35:59 -070041#define ALWAYS_PLUGGED_CAPACITY 100
Badhri Jagan Sridharan40e1df42015-10-27 10:43:53 -070042#define MILLION 10000000.0
43#define DEFAULT_VBUS_VOLTAGE 5000000
Todd Poynor752faf22013-06-12 13:25:59 -070044
45namespace android {
46
47struct sysfsStringEnumMap {
Mark Salyzyn6f5b47f2014-05-15 15:00:59 -070048 const char* s;
Todd Poynor752faf22013-06-12 13:25:59 -070049 int val;
50};
51
52static int mapSysfsString(const char* str,
53 struct sysfsStringEnumMap map[]) {
54 for (int i = 0; map[i].s; i++)
55 if (!strcmp(str, map[i].s))
56 return map[i].val;
57
58 return -1;
59}
60
Yabin Cuiac3387b2016-02-16 17:19:23 -080061static void initBatteryProperties(BatteryProperties* props) {
62 props->chargerAcOnline = false;
63 props->chargerUsbOnline = false;
64 props->chargerWirelessOnline = false;
65 props->maxChargingCurrent = 0;
Badhri Jagan Sridharan40e1df42015-10-27 10:43:53 -070066 props->maxChargingVoltage = 0;
Yabin Cuiac3387b2016-02-16 17:19:23 -080067 props->batteryStatus = BATTERY_STATUS_UNKNOWN;
68 props->batteryHealth = BATTERY_HEALTH_UNKNOWN;
69 props->batteryPresent = false;
70 props->batteryLevel = 0;
71 props->batteryVoltage = 0;
72 props->batteryTemperature = 0;
73 props->batteryCurrent = 0;
74 props->batteryCycleCount = 0;
75 props->batteryFullCharge = 0;
76 props->batteryTechnology.clear();
77}
78
79BatteryMonitor::BatteryMonitor() : mHealthdConfig(nullptr), mBatteryDevicePresent(false),
80 mAlwaysPluggedDevice(false), mBatteryFixedCapacity(0), mBatteryFixedTemperature(0) {
81 initBatteryProperties(&props);
82}
83
Todd Poynor752faf22013-06-12 13:25:59 -070084int BatteryMonitor::getBatteryStatus(const char* status) {
85 int ret;
86 struct sysfsStringEnumMap batteryStatusMap[] = {
87 { "Unknown", BATTERY_STATUS_UNKNOWN },
88 { "Charging", BATTERY_STATUS_CHARGING },
89 { "Discharging", BATTERY_STATUS_DISCHARGING },
90 { "Not charging", BATTERY_STATUS_NOT_CHARGING },
91 { "Full", BATTERY_STATUS_FULL },
92 { NULL, 0 },
93 };
94
95 ret = mapSysfsString(status, batteryStatusMap);
96 if (ret < 0) {
97 KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status);
98 ret = BATTERY_STATUS_UNKNOWN;
99 }
100
101 return ret;
102}
103
104int BatteryMonitor::getBatteryHealth(const char* status) {
105 int ret;
106 struct sysfsStringEnumMap batteryHealthMap[] = {
107 { "Unknown", BATTERY_HEALTH_UNKNOWN },
108 { "Good", BATTERY_HEALTH_GOOD },
109 { "Overheat", BATTERY_HEALTH_OVERHEAT },
110 { "Dead", BATTERY_HEALTH_DEAD },
111 { "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE },
112 { "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE },
113 { "Cold", BATTERY_HEALTH_COLD },
114 { NULL, 0 },
115 };
116
117 ret = mapSysfsString(status, batteryHealthMap);
118 if (ret < 0) {
119 KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status);
120 ret = BATTERY_HEALTH_UNKNOWN;
121 }
122
123 return ret;
124}
125
126int BatteryMonitor::readFromFile(const String8& path, char* buf, size_t size) {
127 char *cp = NULL;
128
129 if (path.isEmpty())
130 return -1;
131 int fd = open(path.string(), O_RDONLY, 0);
132 if (fd == -1) {
133 KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path.string());
134 return -1;
135 }
136
137 ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size));
138 if (count > 0)
139 cp = (char *)memrchr(buf, '\n', count);
140
141 if (cp)
142 *cp = '\0';
143 else
144 buf[0] = '\0';
145
146 close(fd);
147 return count;
148}
149
150BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
151 const int SIZE = 128;
152 char buf[SIZE];
153 int length = readFromFile(path, buf, SIZE);
154 BatteryMonitor::PowerSupplyType ret;
155 struct sysfsStringEnumMap supplyTypeMap[] = {
156 { "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
157 { "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY },
158 { "UPS", ANDROID_POWER_SUPPLY_TYPE_AC },
159 { "Mains", ANDROID_POWER_SUPPLY_TYPE_AC },
160 { "USB", ANDROID_POWER_SUPPLY_TYPE_USB },
161 { "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },
Johan Redestig32828612016-02-03 13:45:54 +0100162 { "USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC },
Todd Poynor752faf22013-06-12 13:25:59 -0700163 { "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },
164 { "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },
Benson Leung8a4eef62015-10-07 16:12:04 -0700165 { "USB_C", ANDROID_POWER_SUPPLY_TYPE_AC },
166 { "USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC },
167 { "USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB },
Todd Poynor752faf22013-06-12 13:25:59 -0700168 { "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },
169 { NULL, 0 },
170 };
171
172 if (length <= 0)
173 return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
174
175 ret = (BatteryMonitor::PowerSupplyType)mapSysfsString(buf, supplyTypeMap);
Johan Redestig32828612016-02-03 13:45:54 +0100176 if (ret < 0) {
177 KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf);
Todd Poynor752faf22013-06-12 13:25:59 -0700178 ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
Johan Redestig32828612016-02-03 13:45:54 +0100179 }
Todd Poynor752faf22013-06-12 13:25:59 -0700180
181 return ret;
182}
183
184bool BatteryMonitor::getBooleanField(const String8& path) {
185 const int SIZE = 16;
186 char buf[SIZE];
187
188 bool value = false;
189 if (readFromFile(path, buf, SIZE) > 0) {
190 if (buf[0] != '0') {
191 value = true;
192 }
193 }
194
195 return value;
196}
197
198int BatteryMonitor::getIntField(const String8& path) {
199 const int SIZE = 128;
200 char buf[SIZE];
201
202 int value = 0;
203 if (readFromFile(path, buf, SIZE) > 0) {
204 value = strtol(buf, NULL, 0);
205 }
206 return value;
207}
208
209bool BatteryMonitor::update(void) {
Todd Poynor10b235e2013-08-07 15:25:14 -0700210 bool logthis;
Todd Poynor752faf22013-06-12 13:25:59 -0700211
Yabin Cuiac3387b2016-02-16 17:19:23 -0800212 initBatteryProperties(&props);
Todd Poynor752faf22013-06-12 13:25:59 -0700213
Todd Poynorf5d30122013-08-12 17:03:35 -0700214 if (!mHealthdConfig->batteryPresentPath.isEmpty())
215 props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
Todd Poynor752faf22013-06-12 13:25:59 -0700216 else
Todd Poynor6dcc45e2013-10-21 20:26:25 -0700217 props.batteryPresent = mBatteryDevicePresent;
Todd Poynor752faf22013-06-12 13:25:59 -0700218
Todd Poynor3db03a52014-05-21 16:28:13 -0700219 props.batteryLevel = mBatteryFixedCapacity ?
220 mBatteryFixedCapacity :
221 getIntField(mHealthdConfig->batteryCapacityPath);
Todd Poynorf5d30122013-08-12 17:03:35 -0700222 props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
Todd Poynorb45f1f52013-07-30 18:57:16 -0700223
Ruchi Kandoicc338802015-08-24 13:01:16 -0700224 if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
225 props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath) / 1000;
226
227 if (!mHealthdConfig->batteryFullChargePath.isEmpty())
228 props.batteryFullCharge = getIntField(mHealthdConfig->batteryFullChargePath);
229
230 if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
231 props.batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
232
Todd Poynor3db03a52014-05-21 16:28:13 -0700233 props.batteryTemperature = mBatteryFixedTemperature ?
234 mBatteryFixedTemperature :
235 getIntField(mHealthdConfig->batteryTemperaturePath);
Todd Poynor752faf22013-06-12 13:25:59 -0700236
Ruchi Kandoif18ec9f2015-09-28 13:35:59 -0700237 // For devices which do not have battery and are always plugged
238 // into power souce.
239 if (mAlwaysPluggedDevice) {
240 props.chargerAcOnline = true;
241 props.batteryPresent = true;
242 props.batteryStatus = BATTERY_STATUS_CHARGING;
243 props.batteryHealth = BATTERY_HEALTH_GOOD;
244 }
245
Todd Poynor752faf22013-06-12 13:25:59 -0700246 const int SIZE = 128;
247 char buf[SIZE];
248 String8 btech;
249
Todd Poynorf5d30122013-08-12 17:03:35 -0700250 if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0)
Todd Poynor752faf22013-06-12 13:25:59 -0700251 props.batteryStatus = getBatteryStatus(buf);
252
Todd Poynorf5d30122013-08-12 17:03:35 -0700253 if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0)
Todd Poynor752faf22013-06-12 13:25:59 -0700254 props.batteryHealth = getBatteryHealth(buf);
255
Todd Poynorf5d30122013-08-12 17:03:35 -0700256 if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0)
Todd Poynor752faf22013-06-12 13:25:59 -0700257 props.batteryTechnology = String8(buf);
258
259 unsigned int i;
Badhri Jagan Sridharan40e1df42015-10-27 10:43:53 -0700260 double MaxPower = 0;
Todd Poynor752faf22013-06-12 13:25:59 -0700261
262 for (i = 0; i < mChargerNames.size(); i++) {
263 String8 path;
264 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
265 mChargerNames[i].string());
266
267 if (readFromFile(path, buf, SIZE) > 0) {
268 if (buf[0] != '0') {
269 path.clear();
270 path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
271 mChargerNames[i].string());
272 switch(readPowerSupplyType(path)) {
273 case ANDROID_POWER_SUPPLY_TYPE_AC:
274 props.chargerAcOnline = true;
275 break;
276 case ANDROID_POWER_SUPPLY_TYPE_USB:
277 props.chargerUsbOnline = true;
278 break;
279 case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
280 props.chargerWirelessOnline = true;
281 break;
282 default:
283 KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
284 mChargerNames[i].string());
285 }
Adrian Roosd5fe6672015-07-10 13:11:01 -0700286 path.clear();
287 path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
288 mChargerNames[i].string());
Badhri Jagan Sridharan40e1df42015-10-27 10:43:53 -0700289 int ChargingCurrent =
290 (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
291
292 path.clear();
293 path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
294 mChargerNames[i].string());
295
296 int ChargingVoltage =
297 (access(path.string(), R_OK) == 0) ? getIntField(path) :
298 DEFAULT_VBUS_VOLTAGE;
299
300 double power = ((double)ChargingCurrent / MILLION) *
301 ((double)ChargingVoltage / MILLION);
302 if (MaxPower < power) {
303 props.maxChargingCurrent = ChargingCurrent;
304 props.maxChargingVoltage = ChargingVoltage;
305 MaxPower = power;
Adrian Roosd5fe6672015-07-10 13:11:01 -0700306 }
Todd Poynor752faf22013-06-12 13:25:59 -0700307 }
308 }
309 }
310
Todd Poynor10b235e2013-08-07 15:25:14 -0700311 logthis = !healthd_board_battery_update(&props);
Todd Poynorb45f1f52013-07-30 18:57:16 -0700312
Todd Poynor10b235e2013-08-07 15:25:14 -0700313 if (logthis) {
314 char dmesgline[256];
Ruchi Kandoicc338802015-08-24 13:01:16 -0700315 size_t len;
Todd Poynorc15e9932013-10-22 18:21:27 -0700316 if (props.batteryPresent) {
Todd Poynor6dcc45e2013-10-21 20:26:25 -0700317 snprintf(dmesgline, sizeof(dmesgline),
Todd Poynor10b235e2013-08-07 15:25:14 -0700318 "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
319 props.batteryLevel, props.batteryVoltage,
320 props.batteryTemperature < 0 ? "-" : "",
321 abs(props.batteryTemperature / 10),
322 abs(props.batteryTemperature % 10), props.batteryHealth,
323 props.batteryStatus);
Todd Poynorc15e9932013-10-22 18:21:27 -0700324
Ruchi Kandoicc338802015-08-24 13:01:16 -0700325 len = strlen(dmesgline);
Todd Poynorc15e9932013-10-22 18:21:27 -0700326 if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
Ruchi Kandoicc338802015-08-24 13:01:16 -0700327 len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
328 " c=%d", props.batteryCurrent);
329 }
Todd Poynorc15e9932013-10-22 18:21:27 -0700330
Ruchi Kandoicc338802015-08-24 13:01:16 -0700331 if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
332 len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
333 " fc=%d", props.batteryFullCharge);
334 }
335
336 if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
337 len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
338 " cc=%d", props.batteryCycleCount);
Todd Poynorc15e9932013-10-22 18:21:27 -0700339 }
340 } else {
Todd Poynor6dcc45e2013-10-21 20:26:25 -0700341 snprintf(dmesgline, sizeof(dmesgline),
342 "battery none");
Todd Poynor10b235e2013-08-07 15:25:14 -0700343 }
344
Ruchi Kandoicc338802015-08-24 13:01:16 -0700345 len = strlen(dmesgline);
Mark Salyzynacb1ddf2015-07-23 09:22:50 -0700346 snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
347 props.chargerAcOnline ? "a" : "",
348 props.chargerUsbOnline ? "u" : "",
349 props.chargerWirelessOnline ? "w" : "");
350
Mark Salyzynacb1ddf2015-07-23 09:22:50 -0700351 KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
Todd Poynorb45f1f52013-07-30 18:57:16 -0700352 }
353
Todd Poynorc7464c92013-09-10 12:40:00 -0700354 healthd_mode_ops->battery_update(&props);
Todd Poynor752faf22013-06-12 13:25:59 -0700355 return props.chargerAcOnline | props.chargerUsbOnline |
356 props.chargerWirelessOnline;
357}
358
Yabin Cuib0580d72016-02-19 18:03:23 -0800359int BatteryMonitor::getChargeStatus() {
360 int result = BATTERY_STATUS_UNKNOWN;
361 if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
362 char buf[128];
363 if (readFromFile(mHealthdConfig->batteryStatusPath, buf, sizeof(buf)) > 0) {
364 result = getBatteryStatus(buf);
365 }
366 }
367 return result;
368}
369
Todd Poynorc133b712013-08-14 17:39:13 -0700370status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
371 status_t ret = BAD_VALUE;
372
Todd Poynor8f132af2014-05-08 17:15:45 -0700373 val->valueInt64 = LONG_MIN;
374
Todd Poynorc133b712013-08-14 17:39:13 -0700375 switch(id) {
376 case BATTERY_PROP_CHARGE_COUNTER:
377 if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
Todd Poynor8f132af2014-05-08 17:15:45 -0700378 val->valueInt64 =
Todd Poynorc133b712013-08-14 17:39:13 -0700379 getIntField(mHealthdConfig->batteryChargeCounterPath);
380 ret = NO_ERROR;
381 } else {
382 ret = NAME_NOT_FOUND;
383 }
384 break;
385
386 case BATTERY_PROP_CURRENT_NOW:
387 if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
Todd Poynor8f132af2014-05-08 17:15:45 -0700388 val->valueInt64 =
Todd Poynorc133b712013-08-14 17:39:13 -0700389 getIntField(mHealthdConfig->batteryCurrentNowPath);
390 ret = NO_ERROR;
391 } else {
392 ret = NAME_NOT_FOUND;
393 }
394 break;
395
Todd Poynorbc102112013-08-27 18:11:49 -0700396 case BATTERY_PROP_CURRENT_AVG:
397 if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
Todd Poynor8f132af2014-05-08 17:15:45 -0700398 val->valueInt64 =
Todd Poynorbc102112013-08-27 18:11:49 -0700399 getIntField(mHealthdConfig->batteryCurrentAvgPath);
400 ret = NO_ERROR;
401 } else {
402 ret = NAME_NOT_FOUND;
403 }
404 break;
405
Paul Lawrence347c8de2014-03-19 15:04:40 -0700406 case BATTERY_PROP_CAPACITY:
407 if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
Todd Poynor8f132af2014-05-08 17:15:45 -0700408 val->valueInt64 =
Paul Lawrence347c8de2014-03-19 15:04:40 -0700409 getIntField(mHealthdConfig->batteryCapacityPath);
410 ret = NO_ERROR;
411 } else {
412 ret = NAME_NOT_FOUND;
413 }
414 break;
415
Todd Poynor8f132af2014-05-08 17:15:45 -0700416 case BATTERY_PROP_ENERGY_COUNTER:
Todd Poynore14b37e2014-05-20 13:54:40 -0700417 if (mHealthdConfig->energyCounter) {
418 ret = mHealthdConfig->energyCounter(&val->valueInt64);
419 } else {
420 ret = NAME_NOT_FOUND;
421 }
Todd Poynor8f132af2014-05-08 17:15:45 -0700422 break;
423
Todd Poynorc133b712013-08-14 17:39:13 -0700424 default:
425 break;
426 }
427
Todd Poynorc133b712013-08-14 17:39:13 -0700428 return ret;
429}
430
Todd Poynor020369d2013-09-18 20:09:33 -0700431void BatteryMonitor::dumpState(int fd) {
432 int v;
433 char vs[128];
434
Badhri Jagan Sridharan40e1df42015-10-27 10:43:53 -0700435 snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
Todd Poynor020369d2013-09-18 20:09:33 -0700436 props.chargerAcOnline, props.chargerUsbOnline,
Badhri Jagan Sridharan40e1df42015-10-27 10:43:53 -0700437 props.chargerWirelessOnline, props.maxChargingCurrent,
438 props.maxChargingVoltage);
Todd Poynor020369d2013-09-18 20:09:33 -0700439 write(fd, vs, strlen(vs));
440 snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
441 props.batteryStatus, props.batteryHealth, props.batteryPresent);
442 write(fd, vs, strlen(vs));
443 snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n",
444 props.batteryLevel, props.batteryVoltage,
445 props.batteryTemperature);
446 write(fd, vs, strlen(vs));
447
448 if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
449 v = getIntField(mHealthdConfig->batteryCurrentNowPath);
450 snprintf(vs, sizeof(vs), "current now: %d\n", v);
451 write(fd, vs, strlen(vs));
452 }
453
454 if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
455 v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
456 snprintf(vs, sizeof(vs), "current avg: %d\n", v);
457 write(fd, vs, strlen(vs));
458 }
459
460 if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
461 v = getIntField(mHealthdConfig->batteryChargeCounterPath);
462 snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
463 write(fd, vs, strlen(vs));
464 }
Ruchi Kandoicc338802015-08-24 13:01:16 -0700465
466 if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
467 snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrent);
468 write(fd, vs, strlen(vs));
469 }
470
471 if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
472 snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
473 write(fd, vs, strlen(vs));
474 }
475
476 if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
477 snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullCharge);
478 write(fd, vs, strlen(vs));
479 }
Todd Poynor020369d2013-09-18 20:09:33 -0700480}
481
Todd Poynorc7464c92013-09-10 12:40:00 -0700482void BatteryMonitor::init(struct healthd_config *hc) {
Todd Poynor752faf22013-06-12 13:25:59 -0700483 String8 path;
Todd Poynor3db03a52014-05-21 16:28:13 -0700484 char pval[PROPERTY_VALUE_MAX];
Todd Poynor752faf22013-06-12 13:25:59 -0700485
Todd Poynorf5d30122013-08-12 17:03:35 -0700486 mHealthdConfig = hc;
Todd Poynor752faf22013-06-12 13:25:59 -0700487 DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
488 if (dir == NULL) {
489 KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
490 } else {
491 struct dirent* entry;
492
493 while ((entry = readdir(dir))) {
494 const char* name = entry->d_name;
495
496 if (!strcmp(name, ".") || !strcmp(name, ".."))
497 continue;
498
Todd Poynor752faf22013-06-12 13:25:59 -0700499 // Look for "type" file in each subdirectory
500 path.clear();
501 path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
502 switch(readPowerSupplyType(path)) {
503 case ANDROID_POWER_SUPPLY_TYPE_AC:
504 case ANDROID_POWER_SUPPLY_TYPE_USB:
505 case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
506 path.clear();
507 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
508 if (access(path.string(), R_OK) == 0)
509 mChargerNames.add(String8(name));
510 break;
511
512 case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
Todd Poynor6dcc45e2013-10-21 20:26:25 -0700513 mBatteryDevicePresent = true;
514
Todd Poynorf5d30122013-08-12 17:03:35 -0700515 if (mHealthdConfig->batteryStatusPath.isEmpty()) {
Todd Poynor752faf22013-06-12 13:25:59 -0700516 path.clear();
Todd Poynorf5d30122013-08-12 17:03:35 -0700517 path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
518 name);
Todd Poynor752faf22013-06-12 13:25:59 -0700519 if (access(path, R_OK) == 0)
Todd Poynorf5d30122013-08-12 17:03:35 -0700520 mHealthdConfig->batteryStatusPath = path;
Todd Poynor752faf22013-06-12 13:25:59 -0700521 }
522
Todd Poynorf5d30122013-08-12 17:03:35 -0700523 if (mHealthdConfig->batteryHealthPath.isEmpty()) {
Todd Poynor752faf22013-06-12 13:25:59 -0700524 path.clear();
Todd Poynorf5d30122013-08-12 17:03:35 -0700525 path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
526 name);
Todd Poynor752faf22013-06-12 13:25:59 -0700527 if (access(path, R_OK) == 0)
Todd Poynorf5d30122013-08-12 17:03:35 -0700528 mHealthdConfig->batteryHealthPath = path;
Todd Poynor752faf22013-06-12 13:25:59 -0700529 }
530
Todd Poynorf5d30122013-08-12 17:03:35 -0700531 if (mHealthdConfig->batteryPresentPath.isEmpty()) {
532 path.clear();
533 path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
534 name);
535 if (access(path, R_OK) == 0)
536 mHealthdConfig->batteryPresentPath = path;
537 }
538
539 if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
540 path.clear();
541 path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
542 name);
543 if (access(path, R_OK) == 0)
544 mHealthdConfig->batteryCapacityPath = path;
545 }
546
547 if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
548 path.clear();
549 path.appendFormat("%s/%s/voltage_now",
550 POWER_SUPPLY_SYSFS_PATH, name);
551 if (access(path, R_OK) == 0) {
552 mHealthdConfig->batteryVoltagePath = path;
553 } else {
554 path.clear();
555 path.appendFormat("%s/%s/batt_vol",
556 POWER_SUPPLY_SYSFS_PATH, name);
557 if (access(path, R_OK) == 0)
558 mHealthdConfig->batteryVoltagePath = path;
559 }
560 }
561
Ruchi Kandoicc338802015-08-24 13:01:16 -0700562 if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
563 path.clear();
564 path.appendFormat("%s/%s/charge_full",
565 POWER_SUPPLY_SYSFS_PATH, name);
566 if (access(path, R_OK) == 0)
567 mHealthdConfig->batteryFullChargePath = path;
568 }
569
Todd Poynorf5d30122013-08-12 17:03:35 -0700570 if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
571 path.clear();
572 path.appendFormat("%s/%s/current_now",
573 POWER_SUPPLY_SYSFS_PATH, name);
574 if (access(path, R_OK) == 0)
575 mHealthdConfig->batteryCurrentNowPath = path;
576 }
577
Ruchi Kandoicc338802015-08-24 13:01:16 -0700578 if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
579 path.clear();
580 path.appendFormat("%s/%s/cycle_count",
581 POWER_SUPPLY_SYSFS_PATH, name);
582 if (access(path, R_OK) == 0)
583 mHealthdConfig->batteryCycleCountPath = path;
584 }
585
Todd Poynorbc102112013-08-27 18:11:49 -0700586 if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
587 path.clear();
588 path.appendFormat("%s/%s/current_avg",
589 POWER_SUPPLY_SYSFS_PATH, name);
590 if (access(path, R_OK) == 0)
591 mHealthdConfig->batteryCurrentAvgPath = path;
592 }
593
Todd Poynorf5d30122013-08-12 17:03:35 -0700594 if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
595 path.clear();
596 path.appendFormat("%s/%s/charge_counter",
597 POWER_SUPPLY_SYSFS_PATH, name);
598 if (access(path, R_OK) == 0)
599 mHealthdConfig->batteryChargeCounterPath = path;
600 }
601
602 if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
603 path.clear();
604 path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
605 name);
606 if (access(path, R_OK) == 0) {
607 mHealthdConfig->batteryTemperaturePath = path;
608 } else {
609 path.clear();
610 path.appendFormat("%s/%s/batt_temp",
611 POWER_SUPPLY_SYSFS_PATH, name);
612 if (access(path, R_OK) == 0)
613 mHealthdConfig->batteryTemperaturePath = path;
614 }
615 }
616
617 if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
618 path.clear();
619 path.appendFormat("%s/%s/technology",
620 POWER_SUPPLY_SYSFS_PATH, name);
621 if (access(path, R_OK) == 0)
622 mHealthdConfig->batteryTechnologyPath = path;
623 }
624
Todd Poynor752faf22013-06-12 13:25:59 -0700625 break;
626
627 case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
628 break;
629 }
630 }
631 closedir(dir);
632 }
633
Ian Pedowitz585ab652015-10-12 19:01:00 -0700634 // This indicates that there is no charger driver registered.
635 // Typically the case for devices which do not have a battery and
636 // and are always plugged into AC mains.
Ruchi Kandoif18ec9f2015-09-28 13:35:59 -0700637 if (!mChargerNames.size()) {
Todd Poynor752faf22013-06-12 13:25:59 -0700638 KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
Ruchi Kandoif18ec9f2015-09-28 13:35:59 -0700639 mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
640 mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
641 mAlwaysPluggedDevice = true;
642 }
Todd Poynor6dcc45e2013-10-21 20:26:25 -0700643 if (!mBatteryDevicePresent) {
Todd Poynorebeb0c02014-09-23 14:54:24 -0700644 KLOG_WARNING(LOG_TAG, "No battery devices found\n");
Todd Poynor6dcc45e2013-10-21 20:26:25 -0700645 hc->periodic_chores_interval_fast = -1;
646 hc->periodic_chores_interval_slow = -1;
647 } else {
648 if (mHealthdConfig->batteryStatusPath.isEmpty())
649 KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
650 if (mHealthdConfig->batteryHealthPath.isEmpty())
651 KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
652 if (mHealthdConfig->batteryPresentPath.isEmpty())
653 KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
654 if (mHealthdConfig->batteryCapacityPath.isEmpty())
655 KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
656 if (mHealthdConfig->batteryVoltagePath.isEmpty())
657 KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
658 if (mHealthdConfig->batteryTemperaturePath.isEmpty())
659 KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
660 if (mHealthdConfig->batteryTechnologyPath.isEmpty())
661 KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
Ruchi Kandoif18ec9f2015-09-28 13:35:59 -0700662 if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
Ruchi Kandoicc338802015-08-24 13:01:16 -0700663 KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
664 if (mHealthdConfig->batteryFullChargePath.isEmpty())
665 KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
666 if (mHealthdConfig->batteryCycleCountPath.isEmpty())
667 KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
Todd Poynor6dcc45e2013-10-21 20:26:25 -0700668 }
Todd Poynor3db03a52014-05-21 16:28:13 -0700669
Ruchi Kandoia78fc232014-07-10 15:06:21 -0700670 if (property_get("ro.boot.fake_battery", pval, NULL) > 0
671 && strtol(pval, NULL, 10) != 0) {
672 mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;
673 mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
674 }
Todd Poynor752faf22013-06-12 13:25:59 -0700675}
676
677}; // namespace android