Home | History | Annotate | Download | only in jni
      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