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 #include <linux/ioctl.h>
     36 
     37 namespace android {
     38 
     39 #define POWER_SUPPLY_PATH "/sys/class/power_supply"
     40 
     41 struct 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 };
     53 static FieldIds gFieldIds;
     54 
     55 struct 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 };
     69 static BatteryManagerConstants gConstants;
     70 
     71 struct 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 };
     82 static PowerSupplyPaths gPaths;
     83 
     84 static int gVoltageDivisor = 1;
     85 
     86 static 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             LOGW("Unknown battery status '%s'", status);
     97             return gConstants.statusUnknown;
     98         }
     99     }
    100 }
    101 
    102 static 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             LOGW("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             LOGW("Unknown battery health[2] '%s'", status);
    129             return gConstants.healthUnknown;
    130         }
    131     }
    132 }
    133 
    134 static 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         LOGE("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 
    157 static 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 
    171 static 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 
    183 static 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 
    197 static 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 
    223 static JNINativeMethod sMethods[] = {
    224      /* name, signature, funcPtr */
    225 	{"native_update", "()V", (void*)android_server_BatteryService_update},
    226 };
    227 
    228 int 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         LOGE("Could not open %s\n", POWER_SUPPLY_PATH);
    236         return -1;
    237     }
    238     while ((entry = readdir(dir))) {
    239         const char* name = entry->d_name;
    240 
    241         // ignore "." and ".."
    242         if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
    243             continue;
    244         }
    245 
    246         char buf[20];
    247         // Look for "type" file in each subdirectory
    248         snprintf(path, sizeof(path), "%s/%s/type", POWER_SUPPLY_PATH, name);
    249         int length = readFromFile(path, buf, sizeof(buf));
    250         if (length > 0) {
    251             if (buf[length - 1] == '\n')
    252                 buf[length - 1] = 0;
    253 
    254             if (strcmp(buf, "Mains") == 0) {
    255                 snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
    256                 if (access(path, R_OK) == 0)
    257                     gPaths.acOnlinePath = strdup(path);
    258             }
    259             else if (strcmp(buf, "USB") == 0) {
    260                 snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
    261                 if (access(path, R_OK) == 0)
    262                     gPaths.usbOnlinePath = strdup(path);
    263             }
    264             else if (strcmp(buf, "Battery") == 0) {
    265                 snprintf(path, sizeof(path), "%s/%s/status", POWER_SUPPLY_PATH, name);
    266                 if (access(path, R_OK) == 0)
    267                     gPaths.batteryStatusPath = strdup(path);
    268                 snprintf(path, sizeof(path), "%s/%s/health", POWER_SUPPLY_PATH, name);
    269                 if (access(path, R_OK) == 0)
    270                     gPaths.batteryHealthPath = strdup(path);
    271                 snprintf(path, sizeof(path), "%s/%s/present", POWER_SUPPLY_PATH, name);
    272                 if (access(path, R_OK) == 0)
    273                     gPaths.batteryPresentPath = strdup(path);
    274                 snprintf(path, sizeof(path), "%s/%s/capacity", POWER_SUPPLY_PATH, name);
    275                 if (access(path, R_OK) == 0)
    276                     gPaths.batteryCapacityPath = strdup(path);
    277 
    278                 snprintf(path, sizeof(path), "%s/%s/voltage_now", POWER_SUPPLY_PATH, name);
    279                 if (access(path, R_OK) == 0) {
    280                     gPaths.batteryVoltagePath = strdup(path);
    281                     // voltage_now is in microvolts, not millivolts
    282                     gVoltageDivisor = 1000;
    283                 } else {
    284                     snprintf(path, sizeof(path), "%s/%s/batt_vol", POWER_SUPPLY_PATH, name);
    285                     if (access(path, R_OK) == 0)
    286                         gPaths.batteryVoltagePath = strdup(path);
    287                 }
    288 
    289                 snprintf(path, sizeof(path), "%s/%s/temp", POWER_SUPPLY_PATH, name);
    290                 if (access(path, R_OK) == 0) {
    291                     gPaths.batteryTemperaturePath = strdup(path);
    292                 } else {
    293                     snprintf(path, sizeof(path), "%s/%s/batt_temp", POWER_SUPPLY_PATH, name);
    294                     if (access(path, R_OK) == 0)
    295                         gPaths.batteryTemperaturePath = strdup(path);
    296                 }
    297 
    298                 snprintf(path, sizeof(path), "%s/%s/technology", POWER_SUPPLY_PATH, name);
    299                 if (access(path, R_OK) == 0)
    300                     gPaths.batteryTechnologyPath = strdup(path);
    301             }
    302         }
    303     }
    304     closedir(dir);
    305 
    306     if (!gPaths.acOnlinePath)
    307         LOGE("acOnlinePath not found");
    308     if (!gPaths.usbOnlinePath)
    309         LOGE("usbOnlinePath not found");
    310     if (!gPaths.batteryStatusPath)
    311         LOGE("batteryStatusPath not found");
    312     if (!gPaths.batteryHealthPath)
    313         LOGE("batteryHealthPath not found");
    314     if (!gPaths.batteryPresentPath)
    315         LOGE("batteryPresentPath not found");
    316     if (!gPaths.batteryCapacityPath)
    317         LOGE("batteryCapacityPath not found");
    318     if (!gPaths.batteryVoltagePath)
    319         LOGE("batteryVoltagePath not found");
    320     if (!gPaths.batteryTemperaturePath)
    321         LOGE("batteryTemperaturePath not found");
    322     if (!gPaths.batteryTechnologyPath)
    323         LOGE("batteryTechnologyPath not found");
    324 
    325     jclass clazz = env->FindClass("com/android/server/BatteryService");
    326 
    327     if (clazz == NULL) {
    328         LOGE("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         LOGE("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