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