/****************************************************************************
* 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 = ; static jint getBatteryStatus(const char* status)
{
// 通过比较首字母来判断电池状态
switch (status[]) {
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[]) {
case 'C': return gConstants.healthCold; // Cold
case 'D': return gConstants.healthDead; // Dead
case 'G': return gConstants.healthGood; // Good
case 'O': {
if (strcmp(status, "Overheat") == ) {
return gConstants.healthOverheat;
} else if (strcmp(status, "Over voltage") == ) {
return gConstants.healthOverVoltage;
}
ALOGW("Unknown battery health[1] '%s'", status);
return gConstants.healthUnknown;
} case 'U': {
if (strcmp(status, "Unspecified failure") == ) {
return gConstants.healthUnspecifiedFailure;
} else if (strcmp(status, "Unknown") == ) {
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 -;
int fd = open(path, O_RDONLY, );
if (fd == -) {
ALOGE("Could not open '%s'", path);
return -;
} ssize_t count = read(fd, buf, size);
if (count > ) {
while (count > && buf[count-] == '\n')
count--;
buf[count] = '\0';
} else {
buf[] = '\0';
} close(fd);
return count;
} // 设置Java属性bool值
static void setBooleanField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
{
const int SIZE = ;
char buf[SIZE]; jboolean value = false;
if (readFromFile(path, buf, SIZE) > ) {
if (buf[] != '') {
value = true;
}
}
env->SetBooleanField(obj, fieldID, value);
} // 设置Java属性int值
static void setIntField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
{
const int SIZE = ;
char buf[SIZE]; jint value = ;
if (readFromFile(path, buf, SIZE) > ) {
value = atoi(buf);
}
env->SetIntField(obj, fieldID, value);
} // 设置Java属性电压属性
static void setVoltageField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
{
const int SIZE = ;
char buf[SIZE]; jint value = ;
if (readFromFile(path, buf, SIZE) > ) {
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[];
// 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, "");
if (!strcmp(prop, "")){
env->SetBooleanField(obj, gFieldIds.mAcOnline, true);
env->SetIntField(obj, gFieldIds.mBatteryLevel, );
} const int SIZE = ;
char buf[SIZE]; // 获取对应路径下的电池数据
if (readFromFile(gPaths.batteryStatusPath, buf, SIZE) > )
env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
else
env->SetIntField(obj, gFieldIds.mBatteryStatus,
gConstants.statusUnknown); if (readFromFile(gPaths.batteryHealthPath, buf, SIZE) > )
env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf)); if (readFromFile(gPaths.batteryTechnologyPath, buf, SIZE) > )
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[] == '.' && (name[] == || (name[] == '.' && name[] == ))) {
continue;
} char buf[];
// 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 > ) {
if (buf[length - ] == '\n')
buf[length - ] = ; if (strcmp(buf, "Mains") == ) {
snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
if (access(path, R_OK) == )
gPaths.acOnlinePath = strdup(path);
}
else if (strcmp(buf, "USB") == ) {
snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
if (access(path, R_OK) == )
gPaths.usbOnlinePath = strdup(path);
}
else if (strcmp(buf, "Wireless") == ) {
snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
if (access(path, R_OK) == )
gPaths.wirelessOnlinePath = strdup(path);
}
/**
* 就是这里了,获取对应的文件路径
*/
else if (strcmp(buf, "Battery") == ) {
snprintf(path, sizeof(path), "%s/%s/status", POWER_SUPPLY_PATH, name);
if (access(path, R_OK) == )
gPaths.batteryStatusPath = strdup(path);
snprintf(path, sizeof(path), "%s/%s/health", POWER_SUPPLY_PATH, name);
if (access(path, R_OK) == )
gPaths.batteryHealthPath = strdup(path);
snprintf(path, sizeof(path), "%s/%s/present", POWER_SUPPLY_PATH, name);
if (access(path, R_OK) == )
gPaths.batteryPresentPath = strdup(path);
snprintf(path, sizeof(path), "%s/%s/capacity", POWER_SUPPLY_PATH, name);
if (access(path, R_OK) == )
gPaths.batteryCapacityPath = strdup(path); snprintf(path, sizeof(path), "%s/%s/voltage_now", POWER_SUPPLY_PATH, name);
if (access(path, R_OK) == ) {
gPaths.batteryVoltagePath = strdup(path);
// voltage_now is in microvolts, not millivolts
gVoltageDivisor = ;
} else {
snprintf(path, sizeof(path), "%s/%s/batt_vol", POWER_SUPPLY_PATH, name);
if (access(path, R_OK) == )
gPaths.batteryVoltagePath = strdup(path);
} snprintf(path, sizeof(path), "%s/%s/temp", POWER_SUPPLY_PATH, name);
if (access(path, R_OK) == ) {
gPaths.batteryTemperaturePath = strdup(path);
} else {
snprintf(path, sizeof(path), "%s/%s/batt_temp", POWER_SUPPLY_PATH, name);
if (access(path, R_OK) == )
gPaths.batteryTemperaturePath = strdup(path);
} snprintf(path, sizeof(path), "%s/%s/technology", POWER_SUPPLY_PATH, name);
if (access(path, R_OK) == )
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 -;
} // 获取属性(域)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 -;
} /**
* 获取一些默认值(敞亮),这些值在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 */