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