blob: 22f77136fcff6eafd0b5016a53b8493811ecaae9 [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;
179
180 if (!mBatteryPresentPath.isEmpty())
181 props.batteryPresent = getBooleanField(mBatteryPresentPath);
182 else
183 props.batteryPresent = true;
184
185 props.batteryLevel = getIntField(mBatteryCapacityPath);
186 props.batteryVoltage = getIntField(mBatteryVoltagePath) / 1000;
187 props.batteryTemperature = getIntField(mBatteryTemperaturePath);
188
189 const int SIZE = 128;
190 char buf[SIZE];
191 String8 btech;
192
193 if (readFromFile(mBatteryStatusPath, buf, SIZE) > 0)
194 props.batteryStatus = getBatteryStatus(buf);
195
196 if (readFromFile(mBatteryHealthPath, buf, SIZE) > 0)
197 props.batteryHealth = getBatteryHealth(buf);
198
199 if (readFromFile(mBatteryTechnologyPath, buf, SIZE) > 0)
200 props.batteryTechnology = String8(buf);
201
202 unsigned int i;
203
204 for (i = 0; i < mChargerNames.size(); i++) {
205 String8 path;
206 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
207 mChargerNames[i].string());
208
209 if (readFromFile(path, buf, SIZE) > 0) {
210 if (buf[0] != '0') {
211 path.clear();
212 path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
213 mChargerNames[i].string());
214 switch(readPowerSupplyType(path)) {
215 case ANDROID_POWER_SUPPLY_TYPE_AC:
216 props.chargerAcOnline = true;
217 break;
218 case ANDROID_POWER_SUPPLY_TYPE_USB:
219 props.chargerUsbOnline = true;
220 break;
221 case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
222 props.chargerWirelessOnline = true;
223 break;
224 default:
225 KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
226 mChargerNames[i].string());
227 }
228 }
229 }
230 }
231
232 KLOG_INFO(LOG_TAG, "battery l=%d v=%d t=%s%d.%d h=%d st=%d chg=%s%s%s\n",
233 props.batteryLevel, props.batteryVoltage,
234 props.batteryTemperature < 0 ? "-" : "",
235 abs(props.batteryTemperature / 10),
236 abs(props.batteryTemperature % 10), props.batteryHealth,
237 props.batteryStatus,
238 props.chargerAcOnline ? "a" : "",
239 props.chargerUsbOnline ? "u" : "",
240 props.chargerWirelessOnline ? "w" : "");
241
242 if (mBatteryPropertiesRegistrar != NULL)
243 mBatteryPropertiesRegistrar->notifyListeners(props);
244
245 return props.chargerAcOnline | props.chargerUsbOnline |
246 props.chargerWirelessOnline;
247}
248
249void BatteryMonitor::init(bool nosvcmgr) {
250 String8 path;
251
252 DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
253 if (dir == NULL) {
254 KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
255 } else {
256 struct dirent* entry;
257
258 while ((entry = readdir(dir))) {
259 const char* name = entry->d_name;
260
261 if (!strcmp(name, ".") || !strcmp(name, ".."))
262 continue;
263
264 char buf[20];
265 // Look for "type" file in each subdirectory
266 path.clear();
267 path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
268 switch(readPowerSupplyType(path)) {
269 case ANDROID_POWER_SUPPLY_TYPE_AC:
270 case ANDROID_POWER_SUPPLY_TYPE_USB:
271 case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
272 path.clear();
273 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
274 if (access(path.string(), R_OK) == 0)
275 mChargerNames.add(String8(name));
276 break;
277
278 case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
279 path.clear();
280 path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH, name);
281 if (access(path, R_OK) == 0)
282 mBatteryStatusPath = path;
283 path.clear();
284 path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH, name);
285 if (access(path, R_OK) == 0)
286 mBatteryHealthPath = path;
287 path.clear();
288 path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH, name);
289 if (access(path, R_OK) == 0)
290 mBatteryPresentPath = path;
291 path.clear();
292 path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH, name);
293 if (access(path, R_OK) == 0)
294 mBatteryCapacityPath = path;
295
296 path.clear();
297 path.appendFormat("%s/%s/voltage_now", POWER_SUPPLY_SYSFS_PATH, name);
298 if (access(path, R_OK) == 0) {
299 mBatteryVoltagePath = path;
300 } else {
301 path.clear();
302 path.appendFormat("%s/%s/batt_vol", POWER_SUPPLY_SYSFS_PATH, name);
303 if (access(path, R_OK) == 0)
304 mBatteryVoltagePath = path;
305 }
306
307 path.clear();
308 path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH, name);
309 if (access(path, R_OK) == 0) {
310 mBatteryTemperaturePath = path;
311 } else {
312 path.clear();
313 path.appendFormat("%s/%s/batt_temp", POWER_SUPPLY_SYSFS_PATH, name);
314 if (access(path, R_OK) == 0)
315 mBatteryTemperaturePath = path;
316 }
317
318 path.clear();
319 path.appendFormat("%s/%s/technology", POWER_SUPPLY_SYSFS_PATH, name);
320 if (access(path, R_OK) == 0)
321 mBatteryTechnologyPath = path;
322 break;
323
324 case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
325 break;
326 }
327 }
328 closedir(dir);
329 }
330
331 if (!mChargerNames.size())
332 KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
333 if (mBatteryStatusPath.isEmpty())
334 KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
335 if (mBatteryHealthPath.isEmpty())
336 KLOG_WARNING("BatteryHealthPath not found\n");
337 if (mBatteryPresentPath.isEmpty())
338 KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
339 if (mBatteryCapacityPath.isEmpty())
340 KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
341 if (mBatteryVoltagePath.isEmpty())
342 KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
343 if (mBatteryTemperaturePath.isEmpty())
344 KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
345 if (mBatteryTechnologyPath.isEmpty())
346 KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
347
348 if (nosvcmgr == false) {
349 mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(this);
350 mBatteryPropertiesRegistrar->publish();
351 }
352}
353
354}; // namespace android