| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "BatteryService" |
| |
| #include "JNIHelp.h" |
| #include "jni.h" |
| #include "utils/Log.h" |
| #include "utils/misc.h" |
| |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <arpa/inet.h> |
| #include <netinet/in.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <unistd.h> |
| |
| #if HAVE_ANDROID_OS |
| #include <linux/ioctl.h> |
| #endif |
| |
| namespace android { |
| |
| #define AC_ONLINE_PATH "/sys/class/power_supply/ac/online" |
| #define USB_ONLINE_PATH "/sys/class/power_supply/usb/online" |
| #define BATTERY_STATUS_PATH "/sys/class/power_supply/battery/status" |
| #define BATTERY_HEALTH_PATH "/sys/class/power_supply/battery/health" |
| #define BATTERY_PRESENT_PATH "/sys/class/power_supply/battery/present" |
| #define BATTERY_CAPACITY_PATH "/sys/class/power_supply/battery/capacity" |
| #define BATTERY_VOLTAGE_PATH "/sys/class/power_supply/battery/batt_vol" |
| #define BATTERY_TEMPERATURE_PATH "/sys/class/power_supply/battery/batt_temp" |
| #define BATTERY_TECHNOLOGY_PATH "/sys/class/power_supply/battery/technology" |
| |
| struct FieldIds { |
| // members |
| jfieldID mAcOnline; |
| jfieldID mUsbOnline; |
| jfieldID mBatteryStatus; |
| jfieldID mBatteryHealth; |
| jfieldID mBatteryPresent; |
| jfieldID mBatteryLevel; |
| jfieldID mBatteryVoltage; |
| jfieldID mBatteryTemperature; |
| jfieldID mBatteryTechnology; |
| }; |
| static FieldIds gFieldIds; |
| |
| struct BatteryManagerConstants { |
| jint statusUnknown; |
| jint statusCharging; |
| jint statusDischarging; |
| jint statusNotCharging; |
| jint statusFull; |
| jint healthUnknown; |
| jint healthGood; |
| jint healthOverheat; |
| jint healthDead; |
| jint healthOverVoltage; |
| jint healthUnspecifiedFailure; |
| }; |
| static BatteryManagerConstants gConstants; |
| |
| static jint getBatteryStatus(const char* status) |
| { |
| switch (status[0]) { |
| case 'C': return gConstants.statusCharging; // Charging |
| case 'D': return gConstants.statusDischarging; // Discharging |
| case 'F': return gConstants.statusFull; // Not charging |
| case 'N': return gConstants.statusNotCharging; // Full |
| case 'U': return gConstants.statusUnknown; // Unknown |
| |
| default: { |
| LOGW("Unknown battery status '%s'", status); |
| return gConstants.statusUnknown; |
| } |
| } |
| } |
| |
| static jint getBatteryHealth(const char* status) |
| { |
| switch (status[0]) { |
| case 'D': return gConstants.healthDead; // Dead |
| case 'G': return gConstants.healthGood; // Good |
| case 'O': { |
| if (strcmp(status, "Overheat") == 0) { |
| return gConstants.healthOverheat; |
| } else if (strcmp(status, "Over voltage") == 0) { |
| return gConstants.healthOverVoltage; |
| } |
| LOGW("Unknown battery health[1] '%s'", status); |
| return gConstants.healthUnknown; |
| } |
| |
| case 'U': { |
| if (strcmp(status, "Unspecified failure") == 0) { |
| return gConstants.healthUnspecifiedFailure; |
| } else if (strcmp(status, "Unknown") == 0) { |
| return gConstants.healthUnknown; |
| } |
| // fall through |
| } |
| |
| default: { |
| LOGW("Unknown battery health[2] '%s'", status); |
| return gConstants.healthUnknown; |
| } |
| } |
| } |
| |
| static int readFromFile(const char* path, char* buf, size_t size) |
| { |
| int fd = open(path, O_RDONLY, 0); |
| if (fd == -1) { |
| LOGE("Could not open '%s'", path); |
| return -1; |
| } |
| |
| size_t count = read(fd, buf, size); |
| if (count > 0) { |
| count = (count < size) ? count : size - 1; |
| while (count > 0 && buf[count-1] == '\n') count--; |
| buf[count] = '\0'; |
| } else { |
| buf[0] = '\0'; |
| } |
| |
| close(fd); |
| return count; |
| } |
| |
| static void setBooleanField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID) |
| { |
| const int SIZE = 16; |
| char buf[SIZE]; |
| |
| jboolean value = false; |
| if (readFromFile(path, buf, SIZE) > 0) { |
| if (buf[0] == '1') { |
| value = true; |
| } |
| } |
| env->SetBooleanField(obj, fieldID, value); |
| } |
| |
| static void setIntField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID) |
| { |
| const int SIZE = 128; |
| char buf[SIZE]; |
| |
| jint value = 0; |
| if (readFromFile(path, buf, SIZE) > 0) { |
| value = atoi(buf); |
| } |
| env->SetIntField(obj, fieldID, value); |
| } |
| |
| static void android_server_BatteryService_update(JNIEnv* env, jobject obj) |
| { |
| setBooleanField(env, obj, AC_ONLINE_PATH, gFieldIds.mAcOnline); |
| setBooleanField(env, obj, USB_ONLINE_PATH, gFieldIds.mUsbOnline); |
| setBooleanField(env, obj, BATTERY_PRESENT_PATH, gFieldIds.mBatteryPresent); |
| |
| setIntField(env, obj, BATTERY_CAPACITY_PATH, gFieldIds.mBatteryLevel); |
| setIntField(env, obj, BATTERY_VOLTAGE_PATH, gFieldIds.mBatteryVoltage); |
| setIntField(env, obj, BATTERY_TEMPERATURE_PATH, gFieldIds.mBatteryTemperature); |
| |
| const int SIZE = 128; |
| char buf[SIZE]; |
| |
| if (readFromFile(BATTERY_STATUS_PATH, buf, SIZE) > 0) |
| env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf)); |
| |
| if (readFromFile(BATTERY_HEALTH_PATH, buf, SIZE) > 0) |
| env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf)); |
| |
| if (readFromFile(BATTERY_TECHNOLOGY_PATH, buf, SIZE) > 0) |
| env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF(buf)); |
| } |
| |
| static JNINativeMethod sMethods[] = { |
| /* name, signature, funcPtr */ |
| {"native_update", "()V", (void*)android_server_BatteryService_update}, |
| }; |
| |
| int register_android_server_BatteryService(JNIEnv* env) |
| { |
| jclass clazz = env->FindClass("com/android/server/BatteryService"); |
| |
| if (clazz == NULL) { |
| LOGE("Can't find com/android/server/BatteryService"); |
| return -1; |
| } |
| |
| gFieldIds.mAcOnline = env->GetFieldID(clazz, "mAcOnline", "Z"); |
| gFieldIds.mUsbOnline = env->GetFieldID(clazz, "mUsbOnline", "Z"); |
| gFieldIds.mBatteryStatus = env->GetFieldID(clazz, "mBatteryStatus", "I"); |
| gFieldIds.mBatteryHealth = env->GetFieldID(clazz, "mBatteryHealth", "I"); |
| gFieldIds.mBatteryPresent = env->GetFieldID(clazz, "mBatteryPresent", "Z"); |
| gFieldIds.mBatteryLevel = env->GetFieldID(clazz, "mBatteryLevel", "I"); |
| gFieldIds.mBatteryTechnology = env->GetFieldID(clazz, "mBatteryTechnology", "Ljava/lang/String;"); |
| gFieldIds.mBatteryVoltage = env->GetFieldID(clazz, "mBatteryVoltage", "I"); |
| gFieldIds.mBatteryTemperature = env->GetFieldID(clazz, "mBatteryTemperature", "I"); |
| |
| LOG_FATAL_IF(gFieldIds.mAcOnline == NULL, "Unable to find BatteryService.AC_ONLINE_PATH"); |
| LOG_FATAL_IF(gFieldIds.mUsbOnline == NULL, "Unable to find BatteryService.USB_ONLINE_PATH"); |
| LOG_FATAL_IF(gFieldIds.mBatteryStatus == NULL, "Unable to find BatteryService.BATTERY_STATUS_PATH"); |
| LOG_FATAL_IF(gFieldIds.mBatteryHealth == NULL, "Unable to find BatteryService.BATTERY_HEALTH_PATH"); |
| LOG_FATAL_IF(gFieldIds.mBatteryPresent == NULL, "Unable to find BatteryService.BATTERY_PRESENT_PATH"); |
| LOG_FATAL_IF(gFieldIds.mBatteryLevel == NULL, "Unable to find BatteryService.BATTERY_CAPACITY_PATH"); |
| LOG_FATAL_IF(gFieldIds.mBatteryVoltage == NULL, "Unable to find BatteryService.BATTERY_VOLTAGE_PATH"); |
| LOG_FATAL_IF(gFieldIds.mBatteryTemperature == NULL, "Unable to find BatteryService.BATTERY_TEMPERATURE_PATH"); |
| LOG_FATAL_IF(gFieldIds.mBatteryTechnology == NULL, "Unable to find BatteryService.BATTERY_TECHNOLOGY_PATH"); |
| |
| clazz = env->FindClass("android/os/BatteryManager"); |
| |
| if (clazz == NULL) { |
| LOGE("Can't find android/os/BatteryManager"); |
| return -1; |
| } |
| |
| gConstants.statusUnknown = env->GetStaticIntField(clazz, |
| env->GetStaticFieldID(clazz, "BATTERY_STATUS_UNKNOWN", "I")); |
| |
| gConstants.statusCharging = env->GetStaticIntField(clazz, |
| env->GetStaticFieldID(clazz, "BATTERY_STATUS_CHARGING", "I")); |
| |
| gConstants.statusDischarging = env->GetStaticIntField(clazz, |
| env->GetStaticFieldID(clazz, "BATTERY_STATUS_DISCHARGING", "I")); |
| |
| gConstants.statusNotCharging = env->GetStaticIntField(clazz, |
| env->GetStaticFieldID(clazz, "BATTERY_STATUS_NOT_CHARGING", "I")); |
| |
| gConstants.statusFull = env->GetStaticIntField(clazz, |
| env->GetStaticFieldID(clazz, "BATTERY_STATUS_FULL", "I")); |
| |
| gConstants.healthUnknown = env->GetStaticIntField(clazz, |
| env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNKNOWN", "I")); |
| |
| gConstants.healthGood = env->GetStaticIntField(clazz, |
| env->GetStaticFieldID(clazz, "BATTERY_HEALTH_GOOD", "I")); |
| |
| gConstants.healthOverheat = env->GetStaticIntField(clazz, |
| env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVERHEAT", "I")); |
| |
| gConstants.healthDead = env->GetStaticIntField(clazz, |
| env->GetStaticFieldID(clazz, "BATTERY_HEALTH_DEAD", "I")); |
| |
| gConstants.healthOverVoltage = env->GetStaticIntField(clazz, |
| env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVER_VOLTAGE", "I")); |
| |
| gConstants.healthUnspecifiedFailure = env->GetStaticIntField(clazz, |
| env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNSPECIFIED_FAILURE", "I")); |
| |
| return jniRegisterNativeMethods(env, "com/android/server/BatteryService", sMethods, NELEM(sMethods)); |
| } |
| |
| } /* namespace android */ |