blob: 882d514bd294846049ac7fc58392fb68b6cc1cbf [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
19#include "BatteryMonitor.h"
20#include "BatteryPropertiesRegistrar.h"
21
22#include <dirent.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <batteryservice/BatteryService.h>
29#include <cutils/klog.h>
30#include <utils/String8.h>
31#include <utils/Vector.h>
32
33#define POWER_SUPPLY_SUBSYSTEM "power_supply"
34#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
35
36namespace android {
37
38struct sysfsStringEnumMap {
39 char* s;
40 int val;
41};
42
43static int mapSysfsString(const char* str,
44 struct sysfsStringEnumMap map[]) {
45 for (int i = 0; map[i].s; i++)
46 if (!strcmp(str, map[i].s))
47 return map[i].val;
48
49 return -1;
50}
51
52int BatteryMonitor::getBatteryStatus(const char* status) {
53 int ret;
54 struct sysfsStringEnumMap batteryStatusMap[] = {
55 { "Unknown", BATTERY_STATUS_UNKNOWN },
56 { "Charging", BATTERY_STATUS_CHARGING },
57 { "Discharging", BATTERY_STATUS_DISCHARGING },
58 { "Not charging", BATTERY_STATUS_NOT_CHARGING },
59 { "Full", BATTERY_STATUS_FULL },
60 { NULL, 0 },
61 };
62
63 ret = mapSysfsString(status, batteryStatusMap);
64 if (ret < 0) {
65 KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status);
66 ret = BATTERY_STATUS_UNKNOWN;
67 }
68
69 return ret;
70}
71
72int BatteryMonitor::getBatteryHealth(const char* status) {
73 int ret;
74 struct sysfsStringEnumMap batteryHealthMap[] = {
75 { "Unknown", BATTERY_HEALTH_UNKNOWN },
76 { "Good", BATTERY_HEALTH_GOOD },
77 { "Overheat", BATTERY_HEALTH_OVERHEAT },
78 { "Dead", BATTERY_HEALTH_DEAD },
79 { "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE },
80 { "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE },
81 { "Cold", BATTERY_HEALTH_COLD },
82 { NULL, 0 },
83 };
84
85 ret = mapSysfsString(status, batteryHealthMap);
86 if (ret < 0) {
87 KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status);
88 ret = BATTERY_HEALTH_UNKNOWN;
89 }
90
91 return ret;
92}
93
94int BatteryMonitor::readFromFile(const String8& path, char* buf, size_t size) {
95 char *cp = NULL;
96
97 if (path.isEmpty())
98 return -1;
99 int fd = open(path.string(), O_RDONLY, 0);
100 if (fd == -1) {
101 KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path.string());
102 return -1;
103 }
104
105 ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size));
106 if (count > 0)
107 cp = (char *)memrchr(buf, '\n', count);
108
109 if (cp)
110 *cp = '\0';
111 else
112 buf[0] = '\0';
113
114 close(fd);
115 return count;
116}
117
118BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
119 const int SIZE = 128;
120 char buf[SIZE];
121 int length = readFromFile(path, buf, SIZE);
122 BatteryMonitor::PowerSupplyType ret;
123 struct sysfsStringEnumMap supplyTypeMap[] = {
124 { "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
125 { "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY },
126 { "UPS", ANDROID_POWER_SUPPLY_TYPE_AC },
127 { "Mains", ANDROID_POWER_SUPPLY_TYPE_AC },
128 { "USB", ANDROID_POWER_SUPPLY_TYPE_USB },
129 { "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },
130 { "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },
131 { "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },
132 { "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },
133 { NULL, 0 },
134 };
135
136 if (length <= 0)
137 return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
138
139 ret = (BatteryMonitor::PowerSupplyType)mapSysfsString(buf, supplyTypeMap);
140 if (ret < 0)
141 ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
142
143 return ret;
144}
145
146bool BatteryMonitor::getBooleanField(const String8& path) {
147 const int SIZE = 16;
148 char buf[SIZE];
149
150 bool value = false;
151 if (readFromFile(path, buf, SIZE) > 0) {
152 if (buf[0] != '0') {
153 value = true;
154 }
155 }
156
157 return value;
158}
159
160int BatteryMonitor::getIntField(const String8& path) {
161 const int SIZE = 128;
162 char buf[SIZE];
163
164 int value = 0;
165 if (readFromFile(path, buf, SIZE) > 0) {
166 value = strtol(buf, NULL, 0);
167 }
168 return value;
169}
170
171bool BatteryMonitor::update(void) {
172 struct BatteryProperties props;
173
174 props.chargerAcOnline = false;
175 props.chargerUsbOnline = false;
176 props.chargerWirelessOnline = false;
177 props.batteryStatus = BATTERY_STATUS_UNKNOWN;
178 props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
Todd Poynorb45f1f52013-07-30 18:57:16 -0700179 props.batteryCurrentNow = INT_MIN;
180 props.batteryChargeCounter = INT_MIN;
Todd Poynor752faf22013-06-12 13:25:59 -0700181
182 if (!mBatteryPresentPath.isEmpty())
183 props.batteryPresent = getBooleanField(mBatteryPresentPath);
184 else
185 props.batteryPresent = true;
186
187 props.batteryLevel = getIntField(mBatteryCapacityPath);
188 props.batteryVoltage = getIntField(mBatteryVoltagePath) / 1000;
Todd Poynorb45f1f52013-07-30 18:57:16 -0700189
190 if (!mBatteryCurrentNowPath.isEmpty())
191 props.batteryCurrentNow = getIntField(mBatteryCurrentNowPath) / 1000;
192
193 if (!mBatteryChargeCounterPath.isEmpty())
194 props.batteryChargeCounter = getIntField(mBatteryChargeCounterPath) / 1000;
195
Todd Poynor752faf22013-06-12 13:25:59 -0700196 props.batteryTemperature = getIntField(mBatteryTemperaturePath);
197
198 const int SIZE = 128;
199 char buf[SIZE];
200 String8 btech;
201
202 if (readFromFile(mBatteryStatusPath, buf, SIZE) > 0)
203 props.batteryStatus = getBatteryStatus(buf);
204
205 if (readFromFile(mBatteryHealthPath, buf, SIZE) > 0)
206 props.batteryHealth = getBatteryHealth(buf);
207
208 if (readFromFile(mBatteryTechnologyPath, buf, SIZE) > 0)
209 props.batteryTechnology = String8(buf);
210
211 unsigned int i;
212
213 for (i = 0; i < mChargerNames.size(); i++) {
214 String8 path;
215 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
216 mChargerNames[i].string());
217
218 if (readFromFile(path, buf, SIZE) > 0) {
219 if (buf[0] != '0') {
220 path.clear();
221 path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
222 mChargerNames[i].string());
223 switch(readPowerSupplyType(path)) {
224 case ANDROID_POWER_SUPPLY_TYPE_AC:
225 props.chargerAcOnline = true;
226 break;
227 case ANDROID_POWER_SUPPLY_TYPE_USB:
228 props.chargerUsbOnline = true;
229 break;
230 case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
231 props.chargerWirelessOnline = true;
232 break;
233 default:
234 KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
235 mChargerNames[i].string());
236 }
237 }
238 }
239 }
240
Todd Poynorb45f1f52013-07-30 18:57:16 -0700241 char dmesgline[256];
242 snprintf(dmesgline, sizeof(dmesgline),
243 "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
244 props.batteryLevel, props.batteryVoltage,
245 props.batteryTemperature < 0 ? "-" : "",
246 abs(props.batteryTemperature / 10),
247 abs(props.batteryTemperature % 10), props.batteryHealth,
248 props.batteryStatus);
249
250 if (!mBatteryCurrentNowPath.isEmpty()) {
251 char b[20];
252
253 snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow);
254 strlcat(dmesgline, b, sizeof(dmesgline));
255 }
256
257 KLOG_INFO(LOG_TAG, "%s chg=%s%s%s\n", dmesgline,
Todd Poynor752faf22013-06-12 13:25:59 -0700258 props.chargerAcOnline ? "a" : "",
259 props.chargerUsbOnline ? "u" : "",
260 props.chargerWirelessOnline ? "w" : "");
261
262 if (mBatteryPropertiesRegistrar != NULL)
263 mBatteryPropertiesRegistrar->notifyListeners(props);
264
265 return props.chargerAcOnline | props.chargerUsbOnline |
266 props.chargerWirelessOnline;
267}
268
269void BatteryMonitor::init(bool nosvcmgr) {
270 String8 path;
271
272 DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
273 if (dir == NULL) {
274 KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
275 } else {
276 struct dirent* entry;
277
278 while ((entry = readdir(dir))) {
279 const char* name = entry->d_name;
280
281 if (!strcmp(name, ".") || !strcmp(name, ".."))
282 continue;
283
284 char buf[20];
285 // Look for "type" file in each subdirectory
286 path.clear();
287 path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
288 switch(readPowerSupplyType(path)) {
289 case ANDROID_POWER_SUPPLY_TYPE_AC:
290 case ANDROID_POWER_SUPPLY_TYPE_USB:
291 case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
292 path.clear();
293 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
294 if (access(path.string(), R_OK) == 0)
295 mChargerNames.add(String8(name));
296 break;
297
298 case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
299 path.clear();
300 path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH, name);
301 if (access(path, R_OK) == 0)
302 mBatteryStatusPath = path;
303 path.clear();
304 path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH, name);
305 if (access(path, R_OK) == 0)
306 mBatteryHealthPath = path;
307 path.clear();
308 path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH, name);
309 if (access(path, R_OK) == 0)
310 mBatteryPresentPath = path;
311 path.clear();
312 path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH, name);
313 if (access(path, R_OK) == 0)
314 mBatteryCapacityPath = path;
315
316 path.clear();
317 path.appendFormat("%s/%s/voltage_now", POWER_SUPPLY_SYSFS_PATH, name);
318 if (access(path, R_OK) == 0) {
319 mBatteryVoltagePath = path;
320 } else {
321 path.clear();
322 path.appendFormat("%s/%s/batt_vol", POWER_SUPPLY_SYSFS_PATH, name);
323 if (access(path, R_OK) == 0)
324 mBatteryVoltagePath = path;
325 }
326
327 path.clear();
Todd Poynorb45f1f52013-07-30 18:57:16 -0700328 path.appendFormat("%s/%s/current_now", POWER_SUPPLY_SYSFS_PATH, name);
329 if (access(path, R_OK) == 0)
330 mBatteryCurrentNowPath = path;
331
332 path.clear();
333 path.appendFormat("%s/%s/charge_counter", POWER_SUPPLY_SYSFS_PATH, name);
334 if (access(path, R_OK) == 0)
335 mBatteryChargeCounterPath = path;
336
337 path.clear();
Todd Poynor752faf22013-06-12 13:25:59 -0700338 path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH, name);
339 if (access(path, R_OK) == 0) {
340 mBatteryTemperaturePath = path;
341 } else {
342 path.clear();
343 path.appendFormat("%s/%s/batt_temp", POWER_SUPPLY_SYSFS_PATH, name);
344 if (access(path, R_OK) == 0)
345 mBatteryTemperaturePath = path;
346 }
347
348 path.clear();
349 path.appendFormat("%s/%s/technology", POWER_SUPPLY_SYSFS_PATH, name);
350 if (access(path, R_OK) == 0)
351 mBatteryTechnologyPath = path;
352 break;
353
354 case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
355 break;
356 }
357 }
358 closedir(dir);
359 }
360
361 if (!mChargerNames.size())
362 KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
363 if (mBatteryStatusPath.isEmpty())
364 KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
365 if (mBatteryHealthPath.isEmpty())
366 KLOG_WARNING("BatteryHealthPath not found\n");
367 if (mBatteryPresentPath.isEmpty())
368 KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
369 if (mBatteryCapacityPath.isEmpty())
370 KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
371 if (mBatteryVoltagePath.isEmpty())
372 KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
373 if (mBatteryTemperaturePath.isEmpty())
374 KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
375 if (mBatteryTechnologyPath.isEmpty())
376 KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
377
378 if (nosvcmgr == false) {
379 mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(this);
380 mBatteryPropertiesRegistrar->publish();
381 }
382}
383
384}; // namespace android