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 #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 
     37 namespace android {
     38 
     39 struct sysfsStringEnumMap {
     40     char* s;
     41     int val;
     42 };
     43 
     44 static 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 
     53 int 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 
     73 int 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 
     95 int 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 
    119 BatteryMonitor::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 
    147 bool 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 
    161 int 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 
    172 bool 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 (!mHealthdConfig->batteryPresentPath.isEmpty())
    185         props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
    186     else
    187         props.batteryPresent = true;
    188 
    189     props.batteryLevel = getIntField(mHealthdConfig->batteryCapacityPath);
    190     props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
    191 
    192     if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
    193         props.batteryCurrentNow = getIntField(mHealthdConfig->batteryCurrentNowPath);
    194 
    195     if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
    196         props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
    197 
    198     props.batteryTemperature = getIntField(mHealthdConfig->batteryTemperaturePath);
    199 
    200     const int SIZE = 128;
    201     char buf[SIZE];
    202     String8 btech;
    203 
    204     if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0)
    205         props.batteryStatus = getBatteryStatus(buf);
    206 
    207     if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0)
    208         props.batteryHealth = getBatteryHealth(buf);
    209 
    210     if (readFromFile(mHealthdConfig->batteryTechnologyPath, 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 (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
    256             char b[20];
    257 
    258             snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow / 1000);
    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 
    275 void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) {
    276     String8 path;
    277 
    278     mHealthdConfig = hc;
    279     DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
    280     if (dir == NULL) {
    281         KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
    282     } else {
    283         struct dirent* entry;
    284 
    285         while ((entry = readdir(dir))) {
    286             const char* name = entry->d_name;
    287 
    288             if (!strcmp(name, ".") || !strcmp(name, ".."))
    289                 continue;
    290 
    291             char buf[20];
    292             // Look for "type" file in each subdirectory
    293             path.clear();
    294             path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
    295             switch(readPowerSupplyType(path)) {
    296             case ANDROID_POWER_SUPPLY_TYPE_AC:
    297             case ANDROID_POWER_SUPPLY_TYPE_USB:
    298             case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
    299                 path.clear();
    300                 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
    301                 if (access(path.string(), R_OK) == 0)
    302                     mChargerNames.add(String8(name));
    303                 break;
    304 
    305             case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
    306                 if (mHealthdConfig->batteryStatusPath.isEmpty()) {
    307                     path.clear();
    308                     path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
    309                                       name);
    310                     if (access(path, R_OK) == 0)
    311                         mHealthdConfig->batteryStatusPath = path;
    312                 }
    313 
    314                 if (mHealthdConfig->batteryHealthPath.isEmpty()) {
    315                     path.clear();
    316                     path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
    317                                       name);
    318                     if (access(path, R_OK) == 0)
    319                         mHealthdConfig->batteryHealthPath = path;
    320                 }
    321 
    322                 if (mHealthdConfig->batteryPresentPath.isEmpty()) {
    323                     path.clear();
    324                     path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
    325                                       name);
    326                     if (access(path, R_OK) == 0)
    327                         mHealthdConfig->batteryPresentPath = path;
    328                 }
    329 
    330                 if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
    331                     path.clear();
    332                     path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
    333                                       name);
    334                     if (access(path, R_OK) == 0)
    335                         mHealthdConfig->batteryCapacityPath = path;
    336                 }
    337 
    338                 if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
    339                     path.clear();
    340                     path.appendFormat("%s/%s/voltage_now",
    341                                       POWER_SUPPLY_SYSFS_PATH, name);
    342                     if (access(path, R_OK) == 0) {
    343                         mHealthdConfig->batteryVoltagePath = path;
    344                     } else {
    345                         path.clear();
    346                         path.appendFormat("%s/%s/batt_vol",
    347                                           POWER_SUPPLY_SYSFS_PATH, name);
    348                         if (access(path, R_OK) == 0)
    349                             mHealthdConfig->batteryVoltagePath = path;
    350                     }
    351                 }
    352 
    353                 if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
    354                     path.clear();
    355                     path.appendFormat("%s/%s/current_now",
    356                                       POWER_SUPPLY_SYSFS_PATH, name);
    357                     if (access(path, R_OK) == 0)
    358                         mHealthdConfig->batteryCurrentNowPath = path;
    359                 }
    360 
    361                 if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
    362                     path.clear();
    363                     path.appendFormat("%s/%s/charge_counter",
    364                                       POWER_SUPPLY_SYSFS_PATH, name);
    365                     if (access(path, R_OK) == 0)
    366                         mHealthdConfig->batteryChargeCounterPath = path;
    367                 }
    368 
    369                 if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
    370                     path.clear();
    371                     path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
    372                                       name);
    373                     if (access(path, R_OK) == 0) {
    374                         mHealthdConfig->batteryTemperaturePath = path;
    375                     } else {
    376                         path.clear();
    377                         path.appendFormat("%s/%s/batt_temp",
    378                                           POWER_SUPPLY_SYSFS_PATH, name);
    379                         if (access(path, R_OK) == 0)
    380                             mHealthdConfig->batteryTemperaturePath = path;
    381                     }
    382                 }
    383 
    384                 if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
    385                     path.clear();
    386                     path.appendFormat("%s/%s/technology",
    387                                       POWER_SUPPLY_SYSFS_PATH, name);
    388                     if (access(path, R_OK) == 0)
    389                         mHealthdConfig->batteryTechnologyPath = path;
    390                 }
    391 
    392                 break;
    393 
    394             case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
    395                 break;
    396             }
    397         }
    398         closedir(dir);
    399     }
    400 
    401     if (!mChargerNames.size())
    402         KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
    403     if (mHealthdConfig->batteryStatusPath.isEmpty())
    404         KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
    405     if (mHealthdConfig->batteryHealthPath.isEmpty())
    406         KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
    407     if (mHealthdConfig->batteryPresentPath.isEmpty())
    408         KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
    409     if (mHealthdConfig->batteryCapacityPath.isEmpty())
    410         KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
    411     if (mHealthdConfig->batteryVoltagePath.isEmpty())
    412         KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
    413     if (mHealthdConfig->batteryTemperaturePath.isEmpty())
    414         KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
    415     if (mHealthdConfig->batteryTechnologyPath.isEmpty())
    416         KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
    417 
    418     if (nosvcmgr == false) {
    419             mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(this);
    420             mBatteryPropertiesRegistrar->publish();
    421     }
    422 }
    423 
    424 }; // namespace android
    425