BatteryMonitor.cpp revision b45f1f5bd00c9a8380960d7c439eb14b3a334f50
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 "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    props.batteryCurrentNow = INT_MIN;
180    props.batteryChargeCounter = INT_MIN;
181
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;
189
190    if (!mBatteryCurrentNowPath.isEmpty())
191        props.batteryCurrentNow = getIntField(mBatteryCurrentNowPath) / 1000;
192
193    if (!mBatteryChargeCounterPath.isEmpty())
194        props.batteryChargeCounter = getIntField(mBatteryChargeCounterPath) / 1000;
195
196    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
241    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,
258              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();
328                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();
338                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
385