zoukankan      html  css  js  c++  java
  • I.MX6 android BatteryService jni hacking

    /****************************************************************************
     *               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] == '
    ')
                count--;
            buf[count] = '';
        } else {
            buf[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
    ", 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] == '
    ')
                        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 */
  • 相关阅读:
    Android开发之《内存对齐》
    Android开发之《libyuv库的使用》
    Android开发之《ffmpeg解码mjpeg视频流》
    Android开发之《USB Camera》
    Cenos配置Android集成化环境, 最终Centos libc库版本过低放弃
    (警告)不要轻易删除libc.so.6,以及误删恢复
    Android开发之《硬件加速》
    EPANET中的typedef使用
    面试
    NSString copy && strong
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/5207865.html
Copyright © 2011-2022 走看看