Smart phones have been developed a lot with more cpu cores, more computation capabilities, with larger and brighter screens, with higher resolution cameras and etc. But battery is one of the areas have no breakthrough. Talking to the battery life, because of the faster cpu/gpu, bigger screen, it even gets worse. Do you still remember the last time you got so mad to find a power supply outlet?
Smart phones use Li-ion batteries. Before Li-ion batteries improve the capacity with the technology breakthrough, how to save the battery life becomes very important as we dont have too much to waste. To save the battery life keeping closely monitoring the battery life is the first step.
Start from Android KK (Kitkat) Android adds the system/core/healthd, which is built into part of Android system image as /sbin/healthd, to monitor the battery state. In this article let's take a close look into healthd to understand how can we monitor the battery state.
What does healthd monitor and tell?
First let's take a look at the healthd logs:
<14>[21867.192464] healthd: battery l=78 v=4024 t=18.8 h=2 st=4 c=-239 chg=a
<14>[21867.421852] healthd: battery l=78 v=4067 t=18.8 h=2 st=2 c=5 chg=a
<14>[21867.448320] healthd: battery l=78 v=4067 t=18.8 h=2 st=2 c=5 chg=a14>14>14>
The healthd monitors the battery state of charge and reports 7 outputs. They are battery level (l=78), voltage (v=4024), battery temperature (t=18.8), health state (h=2), battery status (st=4), charging/discharging current (c=-239), and charging status/charger type (chg=a).
- Battery level: values in 0 ~ 100, means no battery to full battery.
- Battery voltage: voltage in mV, lower battery level, less voltage.
- Battery temperature: temperature in Celsius (°C).
- Health state: values in 1 ~ 7. It is a enumeration with following health state:
- 1 -> Unknown
- 2 -> Good
- 3 -> Overheat
- 4 -> Dead
- 5 -> Over voltage
- 6 -> Unspecified failure
- 7 -> Cold
- Battery status: values in 1 ~ 5. It is a enumeration with following battery status:
- 1 -> Unknown
- 2 -> Charging
- 3 -> Discharging
- 4 -> Not charging
- 5 -> Full
- Charging/discharging current: positive value means discharging current in mV, negative value means charging current in mV.
- Charging status: empty if not on charging. a/u/w otherwise:
- a: charger AC online
- u: charger USB online
- w: charger wireless online
How does healthd read the battery health data?
The battery driver exports the virtual sysfs under /sys/class/power_supply for upper layer to get the battery health data. Let's review the code
https://github.com/android/platform_system_core/blob/master/healthd/BatteryMonitor.cpp to understand how healthd gets the battery health data.
BatteryMonitor::init(...) function: open /sys/class/power_supply directory, read and traverse
void BatteryMonitor::init(struct healthd_config *hc) {
String8 path;
char pval[PROPERTY_VALUE_MAX];
mHealthdConfig = hc;
DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
/* open folder /sys/class/power_supply */
if (dir == NULL) {
KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
} else {
struct dirent* entry;
while ((entry = readdir(dir))) {
/* loop and read files in /sys/class/power_supply */
In sysfs folder /sys/class/power_supply, there is a list of power_supply supported by the given smart phone. In each power_supply folder, there are some attributes. One of the attributes every power_supply shall have is the type. Following code snippets check the type of each power_supply to determine the next things to check:
394 // Look for "type" file in each subdirectory
395 path.clear();
396 path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
397 switch(readPowerSupplyType(path)) {
398 case ANDROID_POWER_SUPPLY_TYPE_AC:
399 case ANDROID_POWER_SUPPLY_TYPE_USB:
400 case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
... ...
407 case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
... ...
506 case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
... ...
If power_supply type is other than Battery or Unknown, then power_supply type should be one of the following:
- ANDROID_POWER_SUPPLY_TYPE_AC
- ANDROID_POWER_SUPPLY_TYPE_USB
- ANDROID_POWER_SUPPLY_TYPE_WIRELESS
and /sys/class/power_supply/%power_supply_type%/online will be further checked to see if such power_supply is plugged to charge the given smart phone.
401 path.clear();
402 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
403 if (access(path.string(), R_OK) == 0)
404 mChargerNames.add(String8(name));
405 break;
... ...
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) {
/* online */
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 }
If power_supply is Battery, it checks more attributes under sysfs folder /sys/class/power_supply/battery, such as status, health, present, capacity, voltage_now/batt_vol, current_now, current_avg, charge_counter, temp/batt_temp, technology.
Read battery level in percentages and battery voltage in mV:
191 props.batteryLevel = mBatteryFixedCapacity ?
192 mBatteryFixedCapacity :
193 getIntField(mHealthdConfig->batteryCapacityPath);
194 props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
Read battery temperature in Celsius (°C):
196 props.batteryTemperature = mBatteryFixedTemperature ?
197 mBatteryFixedTemperature :
198 getIntField(mHealthdConfig->batteryTemperaturePath);
Read battery status:
204 if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0)
205 props.batteryStatus = getBatteryStatus(buf);
57int 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);
Read battery health state:
207 if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0)
208 props.batteryHealth = getBatteryHealth(buf);
77int 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);
After BatteryMonitor.cpp traverse the sysfs folder /sys/class/power_supply and update the attributes, the following code snippets is to notify the applications who concern the battery health state.
275 healthd_mode_ops->battery_update(&props);
It is a callback function registered at the beginning of the health monitoring service. Have to cut here. Will continue with a new article.