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 #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