1/* 2 * Copyright (C) 2008 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 "BatteryService" 18 19#include "JNIHelp.h" 20#include "jni.h" 21#include <utils/Log.h> 22#include <utils/misc.h> 23 24#include <fcntl.h> 25#include <stdio.h> 26#include <string.h> 27#include <sys/types.h> 28#include <sys/socket.h> 29#include <arpa/inet.h> 30#include <netinet/in.h> 31#include <stdlib.h> 32#include <errno.h> 33#include <unistd.h> 34#include <dirent.h> 35#include <linux/ioctl.h> 36 37namespace android { 38 39#define POWER_SUPPLY_PATH "/sys/class/power_supply" 40 41struct FieldIds { 42 // members 43 jfieldID mAcOnline; 44 jfieldID mUsbOnline; 45 jfieldID mBatteryStatus; 46 jfieldID mBatteryHealth; 47 jfieldID mBatteryPresent; 48 jfieldID mBatteryLevel; 49 jfieldID mBatteryVoltage; 50 jfieldID mBatteryTemperature; 51 jfieldID mBatteryTechnology; 52}; 53static FieldIds gFieldIds; 54 55struct BatteryManagerConstants { 56 jint statusUnknown; 57 jint statusCharging; 58 jint statusDischarging; 59 jint statusNotCharging; 60 jint statusFull; 61 jint healthUnknown; 62 jint healthGood; 63 jint healthOverheat; 64 jint healthDead; 65 jint healthOverVoltage; 66 jint healthUnspecifiedFailure; 67 jint healthCold; 68}; 69static BatteryManagerConstants gConstants; 70 71struct PowerSupplyPaths { 72 char* acOnlinePath; 73 char* usbOnlinePath; 74 char* batteryStatusPath; 75 char* batteryHealthPath; 76 char* batteryPresentPath; 77 char* batteryCapacityPath; 78 char* batteryVoltagePath; 79 char* batteryTemperaturePath; 80 char* batteryTechnologyPath; 81}; 82static PowerSupplyPaths gPaths; 83 84static int gVoltageDivisor = 1; 85 86static jint getBatteryStatus(const char* status) 87{ 88 switch (status[0]) { 89 case 'C': return gConstants.statusCharging; // Charging 90 case 'D': return gConstants.statusDischarging; // Discharging 91 case 'F': return gConstants.statusFull; // Not charging 92 case 'N': return gConstants.statusNotCharging; // Full 93 case 'U': return gConstants.statusUnknown; // Unknown 94 95 default: { 96 ALOGW("Unknown battery status '%s'", status); 97 return gConstants.statusUnknown; 98 } 99 } 100} 101 102static jint getBatteryHealth(const char* status) 103{ 104 switch (status[0]) { 105 case 'C': return gConstants.healthCold; // Cold 106 case 'D': return gConstants.healthDead; // Dead 107 case 'G': return gConstants.healthGood; // Good 108 case 'O': { 109 if (strcmp(status, "Overheat") == 0) { 110 return gConstants.healthOverheat; 111 } else if (strcmp(status, "Over voltage") == 0) { 112 return gConstants.healthOverVoltage; 113 } 114 ALOGW("Unknown battery health[1] '%s'", status); 115 return gConstants.healthUnknown; 116 } 117 118 case 'U': { 119 if (strcmp(status, "Unspecified failure") == 0) { 120 return gConstants.healthUnspecifiedFailure; 121 } else if (strcmp(status, "Unknown") == 0) { 122 return gConstants.healthUnknown; 123 } 124 // fall through 125 } 126 127 default: { 128 ALOGW("Unknown battery health[2] '%s'", status); 129 return gConstants.healthUnknown; 130 } 131 } 132} 133 134static int readFromFile(const char* path, char* buf, size_t size) 135{ 136 if (!path) 137 return -1; 138 int fd = open(path, O_RDONLY, 0); 139 if (fd == -1) { 140 ALOGE("Could not open '%s'", path); 141 return -1; 142 } 143 144 ssize_t count = read(fd, buf, size); 145 if (count > 0) { 146 while (count > 0 && buf[count-1] == '\n') 147 count--; 148 buf[count] = '\0'; 149 } else { 150 buf[0] = '\0'; 151 } 152 153 close(fd); 154 return count; 155} 156 157static void setBooleanField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID) 158{ 159 const int SIZE = 16; 160 char buf[SIZE]; 161 162 jboolean value = false; 163 if (readFromFile(path, buf, SIZE) > 0) { 164 if (buf[0] != '0') { 165 value = true; 166 } 167 } 168 env->SetBooleanField(obj, fieldID, value); 169} 170 171static void setIntField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID) 172{ 173 const int SIZE = 128; 174 char buf[SIZE]; 175 176 jint value = 0; 177 if (readFromFile(path, buf, SIZE) > 0) { 178 value = atoi(buf); 179 } 180 env->SetIntField(obj, fieldID, value); 181} 182 183static void setVoltageField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID) 184{ 185 const int SIZE = 128; 186 char buf[SIZE]; 187 188 jint value = 0; 189 if (readFromFile(path, buf, SIZE) > 0) { 190 value = atoi(buf); 191 value /= gVoltageDivisor; 192 } 193 env->SetIntField(obj, fieldID, value); 194} 195 196 197static void android_server_BatteryService_update(JNIEnv* env, jobject obj) 198{ 199 setBooleanField(env, obj, gPaths.acOnlinePath, gFieldIds.mAcOnline); 200 setBooleanField(env, obj, gPaths.usbOnlinePath, gFieldIds.mUsbOnline); 201 setBooleanField(env, obj, gPaths.batteryPresentPath, gFieldIds.mBatteryPresent); 202 203 setIntField(env, obj, gPaths.batteryCapacityPath, gFieldIds.mBatteryLevel); 204 setVoltageField(env, obj, gPaths.batteryVoltagePath, gFieldIds.mBatteryVoltage); 205 setIntField(env, obj, gPaths.batteryTemperaturePath, gFieldIds.mBatteryTemperature); 206 207 const int SIZE = 128; 208 char buf[SIZE]; 209 210 if (readFromFile(gPaths.batteryStatusPath, buf, SIZE) > 0) 211 env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf)); 212 else 213 env->SetIntField(obj, gFieldIds.mBatteryStatus, 214 gConstants.statusUnknown); 215 216 if (readFromFile(gPaths.batteryHealthPath, buf, SIZE) > 0) 217 env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf)); 218 219 if (readFromFile(gPaths.batteryTechnologyPath, buf, SIZE) > 0) 220 env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF(buf)); 221} 222 223static JNINativeMethod sMethods[] = { 224 /* name, signature, funcPtr */ 225 {"native_update", "()V", (void*)android_server_BatteryService_update}, 226}; 227 228int register_android_server_BatteryService(JNIEnv* env) 229{ 230 char path[PATH_MAX]; 231 struct dirent* entry; 232 233 DIR* dir = opendir(POWER_SUPPLY_PATH); 234 if (dir == NULL) { 235 ALOGE("Could not open %s\n", POWER_SUPPLY_PATH); 236 } else { 237 while ((entry = readdir(dir))) { 238 const char* name = entry->d_name; 239 240 // ignore "." and ".." 241 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { 242 continue; 243 } 244 245 char buf[20]; 246 // Look for "type" file in each subdirectory 247 snprintf(path, sizeof(path), "%s/%s/type", POWER_SUPPLY_PATH, name); 248 int length = readFromFile(path, buf, sizeof(buf)); 249 if (length > 0) { 250 if (buf[length - 1] == '\n') 251 buf[length - 1] = 0; 252 253 if (strcmp(buf, "Mains") == 0) { 254 snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name); 255 if (access(path, R_OK) == 0) 256 gPaths.acOnlinePath = strdup(path); 257 } 258 else if (strcmp(buf, "USB") == 0) { 259 snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name); 260 if (access(path, R_OK) == 0) 261 gPaths.usbOnlinePath = strdup(path); 262 } 263 else if (strcmp(buf, "Battery") == 0) { 264 snprintf(path, sizeof(path), "%s/%s/status", POWER_SUPPLY_PATH, name); 265 if (access(path, R_OK) == 0) 266 gPaths.batteryStatusPath = strdup(path); 267 snprintf(path, sizeof(path), "%s/%s/health", POWER_SUPPLY_PATH, name); 268 if (access(path, R_OK) == 0) 269 gPaths.batteryHealthPath = strdup(path); 270 snprintf(path, sizeof(path), "%s/%s/present", POWER_SUPPLY_PATH, name); 271 if (access(path, R_OK) == 0) 272 gPaths.batteryPresentPath = strdup(path); 273 snprintf(path, sizeof(path), "%s/%s/capacity", POWER_SUPPLY_PATH, name); 274 if (access(path, R_OK) == 0) 275 gPaths.batteryCapacityPath = strdup(path); 276 277 snprintf(path, sizeof(path), "%s/%s/voltage_now", POWER_SUPPLY_PATH, name); 278 if (access(path, R_OK) == 0) { 279 gPaths.batteryVoltagePath = strdup(path); 280 // voltage_now is in microvolts, not millivolts 281 gVoltageDivisor = 1000; 282 } else { 283 snprintf(path, sizeof(path), "%s/%s/batt_vol", POWER_SUPPLY_PATH, name); 284 if (access(path, R_OK) == 0) 285 gPaths.batteryVoltagePath = strdup(path); 286 } 287 288 snprintf(path, sizeof(path), "%s/%s/temp", POWER_SUPPLY_PATH, name); 289 if (access(path, R_OK) == 0) { 290 gPaths.batteryTemperaturePath = strdup(path); 291 } else { 292 snprintf(path, sizeof(path), "%s/%s/batt_temp", POWER_SUPPLY_PATH, name); 293 if (access(path, R_OK) == 0) 294 gPaths.batteryTemperaturePath = strdup(path); 295 } 296 297 snprintf(path, sizeof(path), "%s/%s/technology", POWER_SUPPLY_PATH, name); 298 if (access(path, R_OK) == 0) 299 gPaths.batteryTechnologyPath = strdup(path); 300 } 301 } 302 } 303 closedir(dir); 304 } 305 306 if (!gPaths.acOnlinePath) 307 ALOGE("acOnlinePath not found"); 308 if (!gPaths.usbOnlinePath) 309 ALOGE("usbOnlinePath not found"); 310 if (!gPaths.batteryStatusPath) 311 ALOGE("batteryStatusPath not found"); 312 if (!gPaths.batteryHealthPath) 313 ALOGE("batteryHealthPath not found"); 314 if (!gPaths.batteryPresentPath) 315 ALOGE("batteryPresentPath not found"); 316 if (!gPaths.batteryCapacityPath) 317 ALOGE("batteryCapacityPath not found"); 318 if (!gPaths.batteryVoltagePath) 319 ALOGE("batteryVoltagePath not found"); 320 if (!gPaths.batteryTemperaturePath) 321 ALOGE("batteryTemperaturePath not found"); 322 if (!gPaths.batteryTechnologyPath) 323 ALOGE("batteryTechnologyPath not found"); 324 325 jclass clazz = env->FindClass("com/android/server/BatteryService"); 326 327 if (clazz == NULL) { 328 ALOGE("Can't find com/android/server/BatteryService"); 329 return -1; 330 } 331 332 gFieldIds.mAcOnline = env->GetFieldID(clazz, "mAcOnline", "Z"); 333 gFieldIds.mUsbOnline = env->GetFieldID(clazz, "mUsbOnline", "Z"); 334 gFieldIds.mBatteryStatus = env->GetFieldID(clazz, "mBatteryStatus", "I"); 335 gFieldIds.mBatteryHealth = env->GetFieldID(clazz, "mBatteryHealth", "I"); 336 gFieldIds.mBatteryPresent = env->GetFieldID(clazz, "mBatteryPresent", "Z"); 337 gFieldIds.mBatteryLevel = env->GetFieldID(clazz, "mBatteryLevel", "I"); 338 gFieldIds.mBatteryTechnology = env->GetFieldID(clazz, "mBatteryTechnology", "Ljava/lang/String;"); 339 gFieldIds.mBatteryVoltage = env->GetFieldID(clazz, "mBatteryVoltage", "I"); 340 gFieldIds.mBatteryTemperature = env->GetFieldID(clazz, "mBatteryTemperature", "I"); 341 342 LOG_FATAL_IF(gFieldIds.mAcOnline == NULL, "Unable to find BatteryService.AC_ONLINE_PATH"); 343 LOG_FATAL_IF(gFieldIds.mUsbOnline == NULL, "Unable to find BatteryService.USB_ONLINE_PATH"); 344 LOG_FATAL_IF(gFieldIds.mBatteryStatus == NULL, "Unable to find BatteryService.BATTERY_STATUS_PATH"); 345 LOG_FATAL_IF(gFieldIds.mBatteryHealth == NULL, "Unable to find BatteryService.BATTERY_HEALTH_PATH"); 346 LOG_FATAL_IF(gFieldIds.mBatteryPresent == NULL, "Unable to find BatteryService.BATTERY_PRESENT_PATH"); 347 LOG_FATAL_IF(gFieldIds.mBatteryLevel == NULL, "Unable to find BatteryService.BATTERY_CAPACITY_PATH"); 348 LOG_FATAL_IF(gFieldIds.mBatteryVoltage == NULL, "Unable to find BatteryService.BATTERY_VOLTAGE_PATH"); 349 LOG_FATAL_IF(gFieldIds.mBatteryTemperature == NULL, "Unable to find BatteryService.BATTERY_TEMPERATURE_PATH"); 350 LOG_FATAL_IF(gFieldIds.mBatteryTechnology == NULL, "Unable to find BatteryService.BATTERY_TECHNOLOGY_PATH"); 351 352 clazz = env->FindClass("android/os/BatteryManager"); 353 354 if (clazz == NULL) { 355 ALOGE("Can't find android/os/BatteryManager"); 356 return -1; 357 } 358 359 gConstants.statusUnknown = env->GetStaticIntField(clazz, 360 env->GetStaticFieldID(clazz, "BATTERY_STATUS_UNKNOWN", "I")); 361 362 gConstants.statusCharging = env->GetStaticIntField(clazz, 363 env->GetStaticFieldID(clazz, "BATTERY_STATUS_CHARGING", "I")); 364 365 gConstants.statusDischarging = env->GetStaticIntField(clazz, 366 env->GetStaticFieldID(clazz, "BATTERY_STATUS_DISCHARGING", "I")); 367 368 gConstants.statusNotCharging = env->GetStaticIntField(clazz, 369 env->GetStaticFieldID(clazz, "BATTERY_STATUS_NOT_CHARGING", "I")); 370 371 gConstants.statusFull = env->GetStaticIntField(clazz, 372 env->GetStaticFieldID(clazz, "BATTERY_STATUS_FULL", "I")); 373 374 gConstants.healthUnknown = env->GetStaticIntField(clazz, 375 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNKNOWN", "I")); 376 377 gConstants.healthGood = env->GetStaticIntField(clazz, 378 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_GOOD", "I")); 379 380 gConstants.healthOverheat = env->GetStaticIntField(clazz, 381 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVERHEAT", "I")); 382 383 gConstants.healthDead = env->GetStaticIntField(clazz, 384 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_DEAD", "I")); 385 386 gConstants.healthOverVoltage = env->GetStaticIntField(clazz, 387 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVER_VOLTAGE", "I")); 388 389 gConstants.healthUnspecifiedFailure = env->GetStaticIntField(clazz, 390 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNSPECIFIED_FAILURE", "I")); 391 392 gConstants.healthCold = env->GetStaticIntField(clazz, 393 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_COLD", "I")); 394 395 return jniRegisterNativeMethods(env, "com/android/server/BatteryService", sMethods, NELEM(sMethods)); 396} 397 398} /* namespace android */ 399