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