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
22#include <dirent.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <sys/types.h>
28#include <unistd.h>
29
30#include <batteryservice/BatteryService.h>
31#include <cutils/klog.h>
32#include <cutils/properties.h>
33#include <log/log_read.h>
34#include <utils/Errors.h>
35#include <utils/String8.h>
36#include <utils/Vector.h>
37
38#define POWER_SUPPLY_SUBSYSTEM "power_supply"
39#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
40#define FAKE_BATTERY_CAPACITY 42
41#define FAKE_BATTERY_TEMPERATURE 424
42#define ALWAYS_PLUGGED_CAPACITY 100
43
44namespace android {
45
46struct sysfsStringEnumMap {
47    const char* s;
48    int val;
49};
50
51static int mapSysfsString(const char* str,
52                          struct sysfsStringEnumMap map[]) {
53    for (int i = 0; map[i].s; i++)
54        if (!strcmp(str, map[i].s))
55            return map[i].val;
56
57    return -1;
58}
59
60int BatteryMonitor::getBatteryStatus(const char* status) {
61    int ret;
62    struct sysfsStringEnumMap batteryStatusMap[] = {
63        { "Unknown", BATTERY_STATUS_UNKNOWN },
64        { "Charging", BATTERY_STATUS_CHARGING },
65        { "Discharging", BATTERY_STATUS_DISCHARGING },
66        { "Not charging", BATTERY_STATUS_NOT_CHARGING },
67        { "Full", BATTERY_STATUS_FULL },
68        { NULL, 0 },
69    };
70
71    ret = mapSysfsString(status, batteryStatusMap);
72    if (ret < 0) {
73        KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status);
74        ret = BATTERY_STATUS_UNKNOWN;
75    }
76
77    return ret;
78}
79
80int BatteryMonitor::getBatteryHealth(const char* status) {
81    int ret;
82    struct sysfsStringEnumMap batteryHealthMap[] = {
83        { "Unknown", BATTERY_HEALTH_UNKNOWN },
84        { "Good", BATTERY_HEALTH_GOOD },
85        { "Overheat", BATTERY_HEALTH_OVERHEAT },
86        { "Dead", BATTERY_HEALTH_DEAD },
87        { "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE },
88        { "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE },
89        { "Cold", BATTERY_HEALTH_COLD },
90        { NULL, 0 },
91    };
92
93    ret = mapSysfsString(status, batteryHealthMap);
94    if (ret < 0) {
95        KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status);
96        ret = BATTERY_HEALTH_UNKNOWN;
97    }
98
99    return ret;
100}
101
102int BatteryMonitor::readFromFile(const String8& path, char* buf, size_t size) {
103    char *cp = NULL;
104
105    if (path.isEmpty())
106        return -1;
107    int fd = open(path.string(), O_RDONLY, 0);
108    if (fd == -1) {
109        KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path.string());
110        return -1;
111    }
112
113    ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size));
114    if (count > 0)
115            cp = (char *)memrchr(buf, '\n', count);
116
117    if (cp)
118        *cp = '\0';
119    else
120        buf[0] = '\0';
121
122    close(fd);
123    return count;
124}
125
126BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
127    const int SIZE = 128;
128    char buf[SIZE];
129    int length = readFromFile(path, buf, SIZE);
130    BatteryMonitor::PowerSupplyType ret;
131    struct sysfsStringEnumMap supplyTypeMap[] = {
132            { "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
133            { "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY },
134            { "UPS", ANDROID_POWER_SUPPLY_TYPE_AC },
135            { "Mains", ANDROID_POWER_SUPPLY_TYPE_AC },
136            { "USB", ANDROID_POWER_SUPPLY_TYPE_USB },
137            { "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },
138            { "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },
139            { "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },
140            { "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },
141            { NULL, 0 },
142    };
143
144    if (length <= 0)
145        return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
146
147    ret = (BatteryMonitor::PowerSupplyType)mapSysfsString(buf, supplyTypeMap);
148    if (ret < 0)
149        ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
150
151    return ret;
152}
153
154bool BatteryMonitor::getBooleanField(const String8& path) {
155    const int SIZE = 16;
156    char buf[SIZE];
157
158    bool value = false;
159    if (readFromFile(path, buf, SIZE) > 0) {
160        if (buf[0] != '0') {
161            value = true;
162        }
163    }
164
165    return value;
166}
167
168int BatteryMonitor::getIntField(const String8& path) {
169    const int SIZE = 128;
170    char buf[SIZE];
171
172    int value = 0;
173    if (readFromFile(path, buf, SIZE) > 0) {
174        value = strtol(buf, NULL, 0);
175    }
176    return value;
177}
178
179bool BatteryMonitor::update(void) {
180    bool logthis;
181
182    props.chargerAcOnline = false;
183    props.chargerUsbOnline = false;
184    props.chargerWirelessOnline = false;
185    props.batteryStatus = BATTERY_STATUS_UNKNOWN;
186    props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
187    props.maxChargingCurrent = 0;
188
189    if (!mHealthdConfig->batteryPresentPath.isEmpty())
190        props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
191    else
192        props.batteryPresent = mBatteryDevicePresent;
193
194    props.batteryLevel = mBatteryFixedCapacity ?
195        mBatteryFixedCapacity :
196        getIntField(mHealthdConfig->batteryCapacityPath);
197    props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
198
199    props.batteryTemperature = mBatteryFixedTemperature ?
200        mBatteryFixedTemperature :
201        getIntField(mHealthdConfig->batteryTemperaturePath);
202
203    // For devices which do not have battery and are always plugged
204    // into power souce.
205    if (mAlwaysPluggedDevice) {
206        props.chargerAcOnline = true;
207        props.batteryPresent = true;
208        props.batteryStatus = BATTERY_STATUS_CHARGING;
209        props.batteryHealth = BATTERY_HEALTH_GOOD;
210    }
211
212    const int SIZE = 128;
213    char buf[SIZE];
214    String8 btech;
215
216    if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0)
217        props.batteryStatus = getBatteryStatus(buf);
218
219    if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0)
220        props.batteryHealth = getBatteryHealth(buf);
221
222    if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0)
223        props.batteryTechnology = String8(buf);
224
225    unsigned int i;
226
227    for (i = 0; i < mChargerNames.size(); i++) {
228        String8 path;
229        path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
230                          mChargerNames[i].string());
231
232        if (readFromFile(path, buf, SIZE) > 0) {
233            if (buf[0] != '0') {
234                path.clear();
235                path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
236                                  mChargerNames[i].string());
237                switch(readPowerSupplyType(path)) {
238                case ANDROID_POWER_SUPPLY_TYPE_AC:
239                    props.chargerAcOnline = true;
240                    break;
241                case ANDROID_POWER_SUPPLY_TYPE_USB:
242                    props.chargerUsbOnline = true;
243                    break;
244                case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
245                    props.chargerWirelessOnline = true;
246                    break;
247                default:
248                    KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
249                                 mChargerNames[i].string());
250                }
251                path.clear();
252                path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
253                                  mChargerNames[i].string());
254                if (access(path.string(), R_OK) == 0) {
255                    int maxChargingCurrent = getIntField(path);
256                    if (props.maxChargingCurrent < maxChargingCurrent) {
257                        props.maxChargingCurrent = maxChargingCurrent;
258                    }
259                }
260            }
261        }
262    }
263
264    logthis = !healthd_board_battery_update(&props);
265
266    if (logthis) {
267        char dmesgline[256];
268
269        if (props.batteryPresent) {
270            snprintf(dmesgline, sizeof(dmesgline),
271                 "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
272                 props.batteryLevel, props.batteryVoltage,
273                 props.batteryTemperature < 0 ? "-" : "",
274                 abs(props.batteryTemperature / 10),
275                 abs(props.batteryTemperature % 10), props.batteryHealth,
276                 props.batteryStatus);
277
278            if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
279                int c = getIntField(mHealthdConfig->batteryCurrentNowPath);
280                char b[20];
281
282                snprintf(b, sizeof(b), " c=%d", c / 1000);
283                strlcat(dmesgline, b, sizeof(dmesgline));
284            }
285        } else {
286            snprintf(dmesgline, sizeof(dmesgline),
287                 "battery none");
288        }
289
290        size_t len = strlen(dmesgline);
291        snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
292                 props.chargerAcOnline ? "a" : "",
293                 props.chargerUsbOnline ? "u" : "",
294                 props.chargerWirelessOnline ? "w" : "");
295
296        log_time realtime(CLOCK_REALTIME);
297        time_t t = realtime.tv_sec;
298        struct tm *tmp = gmtime(&t);
299        if (tmp) {
300            static const char fmt[] = " %Y-%m-%d %H:%M:%S.XXXXXXXXX UTC";
301            len = strlen(dmesgline);
302            if ((len < (sizeof(dmesgline) - sizeof(fmt) - 8)) // margin
303                    && strftime(dmesgline + len, sizeof(dmesgline) - len,
304                                fmt, tmp)) {
305                char *usec = strchr(dmesgline + len, 'X');
306                if (usec) {
307                    len = usec - dmesgline;
308                    snprintf(dmesgline + len, sizeof(dmesgline) - len,
309                             "%09u", realtime.tv_nsec);
310                    usec[9] = ' ';
311                }
312            }
313        }
314
315        KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
316    }
317
318    healthd_mode_ops->battery_update(&props);
319    return props.chargerAcOnline | props.chargerUsbOnline |
320            props.chargerWirelessOnline;
321}
322
323status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
324    status_t ret = BAD_VALUE;
325
326    val->valueInt64 = LONG_MIN;
327
328    switch(id) {
329    case BATTERY_PROP_CHARGE_COUNTER:
330        if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
331            val->valueInt64 =
332                getIntField(mHealthdConfig->batteryChargeCounterPath);
333            ret = NO_ERROR;
334        } else {
335            ret = NAME_NOT_FOUND;
336        }
337        break;
338
339    case BATTERY_PROP_CURRENT_NOW:
340        if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
341            val->valueInt64 =
342                getIntField(mHealthdConfig->batteryCurrentNowPath);
343            ret = NO_ERROR;
344        } else {
345            ret = NAME_NOT_FOUND;
346        }
347        break;
348
349    case BATTERY_PROP_CURRENT_AVG:
350        if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
351            val->valueInt64 =
352                getIntField(mHealthdConfig->batteryCurrentAvgPath);
353            ret = NO_ERROR;
354        } else {
355            ret = NAME_NOT_FOUND;
356        }
357        break;
358
359    case BATTERY_PROP_CAPACITY:
360        if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
361            val->valueInt64 =
362                getIntField(mHealthdConfig->batteryCapacityPath);
363            ret = NO_ERROR;
364        } else {
365            ret = NAME_NOT_FOUND;
366        }
367        break;
368
369    case BATTERY_PROP_ENERGY_COUNTER:
370        if (mHealthdConfig->energyCounter) {
371            ret = mHealthdConfig->energyCounter(&val->valueInt64);
372        } else {
373            ret = NAME_NOT_FOUND;
374        }
375        break;
376
377    default:
378        break;
379    }
380
381    return ret;
382}
383
384void BatteryMonitor::dumpState(int fd) {
385    int v;
386    char vs[128];
387
388    snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d\n",
389             props.chargerAcOnline, props.chargerUsbOnline,
390             props.chargerWirelessOnline, props.maxChargingCurrent);
391    write(fd, vs, strlen(vs));
392    snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
393             props.batteryStatus, props.batteryHealth, props.batteryPresent);
394    write(fd, vs, strlen(vs));
395    snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n",
396             props.batteryLevel, props.batteryVoltage,
397             props.batteryTemperature);
398    write(fd, vs, strlen(vs));
399
400    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
401        v = getIntField(mHealthdConfig->batteryCurrentNowPath);
402        snprintf(vs, sizeof(vs), "current now: %d\n", v);
403        write(fd, vs, strlen(vs));
404    }
405
406    if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
407        v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
408        snprintf(vs, sizeof(vs), "current avg: %d\n", v);
409        write(fd, vs, strlen(vs));
410    }
411
412    if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
413        v = getIntField(mHealthdConfig->batteryChargeCounterPath);
414        snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
415        write(fd, vs, strlen(vs));
416    }
417}
418
419void BatteryMonitor::init(struct healthd_config *hc) {
420    String8 path;
421    char pval[PROPERTY_VALUE_MAX];
422
423    mHealthdConfig = hc;
424    DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
425    if (dir == NULL) {
426        KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
427    } else {
428        struct dirent* entry;
429
430        while ((entry = readdir(dir))) {
431            const char* name = entry->d_name;
432
433            if (!strcmp(name, ".") || !strcmp(name, ".."))
434                continue;
435
436            // Look for "type" file in each subdirectory
437            path.clear();
438            path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
439            switch(readPowerSupplyType(path)) {
440            case ANDROID_POWER_SUPPLY_TYPE_AC:
441            case ANDROID_POWER_SUPPLY_TYPE_USB:
442            case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
443                path.clear();
444                path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
445                if (access(path.string(), R_OK) == 0)
446                    mChargerNames.add(String8(name));
447                break;
448
449            case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
450                mBatteryDevicePresent = true;
451
452                if (mHealthdConfig->batteryStatusPath.isEmpty()) {
453                    path.clear();
454                    path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
455                                      name);
456                    if (access(path, R_OK) == 0)
457                        mHealthdConfig->batteryStatusPath = path;
458                }
459
460                if (mHealthdConfig->batteryHealthPath.isEmpty()) {
461                    path.clear();
462                    path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
463                                      name);
464                    if (access(path, R_OK) == 0)
465                        mHealthdConfig->batteryHealthPath = path;
466                }
467
468                if (mHealthdConfig->batteryPresentPath.isEmpty()) {
469                    path.clear();
470                    path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
471                                      name);
472                    if (access(path, R_OK) == 0)
473                        mHealthdConfig->batteryPresentPath = path;
474                }
475
476                if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
477                    path.clear();
478                    path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
479                                      name);
480                    if (access(path, R_OK) == 0)
481                        mHealthdConfig->batteryCapacityPath = path;
482                }
483
484                if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
485                    path.clear();
486                    path.appendFormat("%s/%s/voltage_now",
487                                      POWER_SUPPLY_SYSFS_PATH, name);
488                    if (access(path, R_OK) == 0) {
489                        mHealthdConfig->batteryVoltagePath = path;
490                    } else {
491                        path.clear();
492                        path.appendFormat("%s/%s/batt_vol",
493                                          POWER_SUPPLY_SYSFS_PATH, name);
494                        if (access(path, R_OK) == 0)
495                            mHealthdConfig->batteryVoltagePath = path;
496                    }
497                }
498
499                if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
500                    path.clear();
501                    path.appendFormat("%s/%s/current_now",
502                                      POWER_SUPPLY_SYSFS_PATH, name);
503                    if (access(path, R_OK) == 0)
504                        mHealthdConfig->batteryCurrentNowPath = path;
505                }
506
507                if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
508                    path.clear();
509                    path.appendFormat("%s/%s/current_avg",
510                                      POWER_SUPPLY_SYSFS_PATH, name);
511                    if (access(path, R_OK) == 0)
512                        mHealthdConfig->batteryCurrentAvgPath = path;
513                }
514
515                if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
516                    path.clear();
517                    path.appendFormat("%s/%s/charge_counter",
518                                      POWER_SUPPLY_SYSFS_PATH, name);
519                    if (access(path, R_OK) == 0)
520                        mHealthdConfig->batteryChargeCounterPath = path;
521                }
522
523                if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
524                    path.clear();
525                    path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
526                                      name);
527                    if (access(path, R_OK) == 0) {
528                        mHealthdConfig->batteryTemperaturePath = path;
529                    } else {
530                        path.clear();
531                        path.appendFormat("%s/%s/batt_temp",
532                                          POWER_SUPPLY_SYSFS_PATH, name);
533                        if (access(path, R_OK) == 0)
534                            mHealthdConfig->batteryTemperaturePath = path;
535                    }
536                }
537
538                if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
539                    path.clear();
540                    path.appendFormat("%s/%s/technology",
541                                      POWER_SUPPLY_SYSFS_PATH, name);
542                    if (access(path, R_OK) == 0)
543                        mHealthdConfig->batteryTechnologyPath = path;
544                }
545
546                break;
547
548            case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
549                break;
550            }
551        }
552        closedir(dir);
553    }
554
555    // This indicates that there is no charger driver registered.
556    // Typically the case for devices which do not have a battery and
557    // and are always plugged into AC mains.
558    if (!mChargerNames.size()) {
559        KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
560        mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
561        mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
562        mAlwaysPluggedDevice = true;
563    }
564    if (!mBatteryDevicePresent) {
565        KLOG_WARNING(LOG_TAG, "No battery devices found\n");
566        hc->periodic_chores_interval_fast = -1;
567        hc->periodic_chores_interval_slow = -1;
568    } else {
569        if (mHealthdConfig->batteryStatusPath.isEmpty())
570            KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
571        if (mHealthdConfig->batteryHealthPath.isEmpty())
572            KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
573        if (mHealthdConfig->batteryPresentPath.isEmpty())
574            KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
575        if (mHealthdConfig->batteryCapacityPath.isEmpty())
576            KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
577        if (mHealthdConfig->batteryVoltagePath.isEmpty())
578            KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
579        if (mHealthdConfig->batteryTemperaturePath.isEmpty())
580            KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
581        if (mHealthdConfig->batteryTechnologyPath.isEmpty())
582            KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
583    }
584
585    if (property_get("ro.boot.fake_battery", pval, NULL) > 0
586                                               && strtol(pval, NULL, 10) != 0) {
587        mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;
588        mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
589    }
590}
591
592}; // namespace android
593