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