/**************************************************************************** * I.MX6 android BatteryService jni hacking * 声明: * 本文主要是为了知道Android的获取的电源管理的数据的jni是从Linux系统的 * 什么位置获取的,获取的机制是什么。 * * 2016-2-22 深圳 南山平山村 曾剑锋 ***************************************************************************/ /* * 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 <cutils/properties.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> #include <dirent.h> #include <linux/ioctl.h> namespace android { /** * 从下面的路径可知,POWER_SUPPLY_PATH相当于电池管理初始路径。 * root@android:/sys/class/power_supply/bq274xx-0 # ls * capacity * capacity_level * charge_full * charge_full_design * charge_now * current_now * device * power * present * status * subsystem * technology * temp * type * uevent * voltage_now * root@android:/sys/class/power_supply/bq274xx-0 # */ #define POWER_SUPPLY_PATH "/sys/class/power_supply" /** * 为了在C/C++中表示属性和方法,JNI在jni.h头文件中定义了jfieldID和jmethodID类型来分别代表Java对象的属性和方法。我们在访问或是设置Java属性的时候,首先就要先在本地代码取得代表该Java属性的jfieldID,然后才能在本地代码进行Java属性操作。同样的,我们需要调用Java对象方法时,也是需要取得代表该方法的jmethodID才能进行Java方法调用。 */ struct FieldIds { // members jfieldID mAcOnline; jfieldID mUsbOnline; jfieldID mWirelessOnline; 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; jint healthCold; }; static BatteryManagerConstants gConstants; /** * 指向sysfs中电池对应的文件 */ struct PowerSupplyPaths { char* acOnlinePath; char* usbOnlinePath; char* wirelessOnlinePath; char* batteryStatusPath; char* batteryHealthPath; char* batteryPresentPath; char* batteryCapacityPath; char* batteryVoltagePath; char* batteryTemperaturePath; char* batteryTechnologyPath; }; static PowerSupplyPaths gPaths; static int gVoltageDivisor = 1; 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; // Full case 'N': return gConstants.statusNotCharging; // Not charging case 'U': return gConstants.statusUnknown; // Unknown default: { ALOGW("Unknown battery status '%s'", status); return gConstants.statusUnknown; } } } static jint getBatteryHealth(const char* status) { // 通过比较首字母来判断电池状态 switch (status[0]) { case 'C': return gConstants.healthCold; // Cold 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; } ALOGW("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: { ALOGW("Unknown battery health[2] '%s'", status); return gConstants.healthUnknown; } } } // 从文件中读取内容,并剔除最后的换行符 static int readFromFile(const char* path, char* buf, size_t size) { if (!path) return -1; int fd = open(path, O_RDONLY, 0); if (fd == -1) { ALOGE("Could not open '%s'", path); return -1; } ssize_t count = read(fd, buf, size); if (count > 0) { while (count > 0 && buf[count-1] == '\n') count--; buf[count] = '\0'; } else { buf[0] = '\0'; } close(fd); return count; } // 设置Java属性bool值 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] != '0') { value = true; } } env->SetBooleanField(obj, fieldID, value); } // 设置Java属性int值 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); } // 设置Java属性电压属性 static void setVoltageField(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); value /= gVoltageDivisor; } env->SetIntField(obj, fieldID, value); } // 更新电池状态 static void android_server_BatteryService_update(JNIEnv* env, jobject obj) { setBooleanField(env, obj, gPaths.acOnlinePath, gFieldIds.mAcOnline); setBooleanField(env, obj, gPaths.usbOnlinePath, gFieldIds.mUsbOnline); setBooleanField(env, obj, gPaths.wirelessOnlinePath, gFieldIds.mWirelessOnline); setBooleanField(env, obj, gPaths.batteryPresentPath, gFieldIds.mBatteryPresent); setIntField(env, obj, gPaths.batteryCapacityPath, gFieldIds.mBatteryLevel); setVoltageField(env, obj, gPaths.batteryVoltagePath, gFieldIds.mBatteryVoltage); setIntField(env, obj, gPaths.batteryTemperaturePath, gFieldIds.mBatteryTemperature); char prop[5]; // always report AC plug-in and capacity 100% if emulated.battery is set to 1 // 如果在调试的时候希望电池的状态永远为100%,电池永远被插入,那么设置属性sys.emulated.battery为1 property_get("sys.emulated.battery", prop, "0"); if (!strcmp(prop, "1")){ env->SetBooleanField(obj, gFieldIds.mAcOnline, true); env->SetIntField(obj, gFieldIds.mBatteryLevel, 100); } const int SIZE = 128; char buf[SIZE]; // 获取对应路径下的电池数据 if (readFromFile(gPaths.batteryStatusPath, buf, SIZE) > 0) env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf)); else env->SetIntField(obj, gFieldIds.mBatteryStatus, gConstants.statusUnknown); if (readFromFile(gPaths.batteryHealthPath, buf, SIZE) > 0) env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf)); if (readFromFile(gPaths.batteryTechnologyPath, 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}, }; /** * root@android:/sys/class/power_supply/bq274xx-0 # ls * capacity * capacity_level * charge_full * charge_full_design * charge_now * current_now * device * power * present * status * subsystem * technology * temp * type * uevent * voltage_now * root@android:/sys/class/power_supply/bq274xx-0 # */ // 电池管理中默认被Java构造函数调用的函数 int register_android_server_BatteryService(JNIEnv* env) { char path[PATH_MAX]; struct dirent* entry; // 打开电池设备在sysfs中对应的文件夹 DIR* dir = opendir(POWER_SUPPLY_PATH); if (dir == NULL) { ALOGE("Could not open %s\n", POWER_SUPPLY_PATH); } else { // 连续读取子目录,这里可能是考虑到可能有多个电源管理芯片 // 当然,这里也要考虑当前目录(.)和上一级目录的存在(..) while ((entry = readdir(dir))) { // 获取子文件夹的名字,这里是bq274xx-0 const char* name = entry->d_name; // ignore "." and ".." 剔除当前目录和上一级目录 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { continue; } char buf[20]; // Look for "type" file in each subdirectory /** * root@android:/sys/class/power_supply/bq274xx-0 # cat type * Battery ---> type * root@android:/sys/class/power_supply/bq274xx-0 # */ snprintf(path, sizeof(path), "%s/%s/type", POWER_SUPPLY_PATH, name); int length = readFromFile(path, buf, sizeof(buf)); if (length > 0) { if (buf[length - 1] == '\n') buf[length - 1] = 0; if (strcmp(buf, "Mains") == 0) { snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name); if (access(path, R_OK) == 0) gPaths.acOnlinePath = strdup(path); } else if (strcmp(buf, "USB") == 0) { snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name); if (access(path, R_OK) == 0) gPaths.usbOnlinePath = strdup(path); } else if (strcmp(buf, "Wireless") == 0) { snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name); if (access(path, R_OK) == 0) gPaths.wirelessOnlinePath = strdup(path); } /** * 就是这里了,获取对应的文件路径 */ else if (strcmp(buf, "Battery") == 0) { snprintf(path, sizeof(path), "%s/%s/status", POWER_SUPPLY_PATH, name); if (access(path, R_OK) == 0) gPaths.batteryStatusPath = strdup(path); snprintf(path, sizeof(path), "%s/%s/health", POWER_SUPPLY_PATH, name); if (access(path, R_OK) == 0) gPaths.batteryHealthPath = strdup(path); snprintf(path, sizeof(path), "%s/%s/present", POWER_SUPPLY_PATH, name); if (access(path, R_OK) == 0) gPaths.batteryPresentPath = strdup(path); snprintf(path, sizeof(path), "%s/%s/capacity", POWER_SUPPLY_PATH, name); if (access(path, R_OK) == 0) gPaths.batteryCapacityPath = strdup(path); snprintf(path, sizeof(path), "%s/%s/voltage_now", POWER_SUPPLY_PATH, name); if (access(path, R_OK) == 0) { gPaths.batteryVoltagePath = strdup(path); // voltage_now is in microvolts, not millivolts gVoltageDivisor = 1000; } else { snprintf(path, sizeof(path), "%s/%s/batt_vol", POWER_SUPPLY_PATH, name); if (access(path, R_OK) == 0) gPaths.batteryVoltagePath = strdup(path); } snprintf(path, sizeof(path), "%s/%s/temp", POWER_SUPPLY_PATH, name); if (access(path, R_OK) == 0) { gPaths.batteryTemperaturePath = strdup(path); } else { snprintf(path, sizeof(path), "%s/%s/batt_temp", POWER_SUPPLY_PATH, name); if (access(path, R_OK) == 0) gPaths.batteryTemperaturePath = strdup(path); } snprintf(path, sizeof(path), "%s/%s/technology", POWER_SUPPLY_PATH, name); if (access(path, R_OK) == 0) gPaths.batteryTechnologyPath = strdup(path); } } } closedir(dir); } // 判断文件路径是否存在,并给出提示信息 if (!gPaths.acOnlinePath) ALOGE("acOnlinePath not found"); if (!gPaths.usbOnlinePath) ALOGE("usbOnlinePath not found"); if (!gPaths.wirelessOnlinePath) ALOGE("wirelessOnlinePath not found"); if (!gPaths.batteryStatusPath) ALOGE("batteryStatusPath not found"); if (!gPaths.batteryHealthPath) ALOGE("batteryHealthPath not found"); if (!gPaths.batteryPresentPath) ALOGE("batteryPresentPath not found"); if (!gPaths.batteryCapacityPath) ALOGE("batteryCapacityPath not found"); if (!gPaths.batteryVoltagePath) ALOGE("batteryVoltagePath not found"); if (!gPaths.batteryTemperaturePath) ALOGE("batteryTemperaturePath not found"); if (!gPaths.batteryTechnologyPath) ALOGE("batteryTechnologyPath not found"); // 获取类指针 jclass clazz = env->FindClass("com/android/server/BatteryService"); if (clazz == NULL) { ALOGE("Can't find com/android/server/BatteryService"); return -1; } // 获取属性(域)Id gFieldIds.mAcOnline = env->GetFieldID(clazz, "mAcOnline", "Z"); gFieldIds.mUsbOnline = env->GetFieldID(clazz, "mUsbOnline", "Z"); gFieldIds.mWirelessOnline = env->GetFieldID(clazz, "mWirelessOnline", "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.mWirelessOnline == NULL, "Unable to find BatteryService.WIRELESS_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) { ALOGE("Can't find android/os/BatteryManager"); return -1; } /** * 获取一些默认值(敞亮),这些值在android/os/BatteryManager有定义 */ 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")); gConstants.healthCold = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "BATTERY_HEALTH_COLD", "I")); /** * 注册更新函数 */ return jniRegisterNativeMethods(env, "com/android/server/BatteryService", sMethods, NELEM(sMethods)); } } /* namespace android */