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