Android源码笔记--电量

Posted ljt2724960661

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android源码笔记--电量相关的知识,希望对你有一定的参考价值。

          这一节主要了解BatteryService相关知识,在PowerManagerService中调用了BatteryService类的一些接口来获得电池的状态,下面看看BatteryService 是如何获得电池状态数据的。

          BatteryService 类的作用 

          构造方法如下:

 public BatteryService(Context context) 
        super(context);

        mContext = context;
        mHandler = new Handler(true /*async*/);
        mLed = new Led(context, getLocalService(LightsManager.class));
        mBatteryStats = BatteryStatsService.getService();
        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);

		// 读取系统设定的各种低电量报警值 
        mCriticalBatteryLevel = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
        mLowBatteryWarningLevel = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_lowBatteryWarningLevel);
        mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
                com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
        mShutdownBatteryTemperature = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_shutdownBatteryTemperature);

        mBatteryLevelsEventQueue = new ArrayDeque<>();
        mMetricsLogger = new MetricsLogger();

        // 监听下面的设备文件 无效的充电设备 
        if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) 
            UEventObserver invalidChargerObserver = new UEventObserver() 
                @Override
                public void onUEvent(UEvent event) 
                    final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
                    synchronized (mLock) 
                        if (mInvalidCharger != invalidCharger) 
                            mInvalidCharger = invalidCharger;
                        
                    
                
            ;
            invalidChargerObserver.startObserving(
                    "DEVPATH=/devices/virtual/switch/invalid_charger");
        
    

分析: BatteryService的构造方法首先获得BatteryStatsService的对象,BatteryStatsService 主要的功能是收集系统中各个模块和进程的耗电情况。通过 BatteryStatsService 记录的数据,我们可以找到耗电量大的模块然后加以改进。

接下来是读取了系统设定的各种低电量报警值,包括如下:

mCriticalBatteryLevel:表示电量严重不足时的值,低于这个值系统将关闭。
mLowBatteryWarningLevel:表示电量不足时的值,低于这个值系统将发出警告。
mLowBatteryCloseWarningLevel:表示停止电量不足警告的值。电量高于这个值后系统将停止电量不足的警告。
 mShutdownBatteryTemperature:表示电池温度太高的值。高于这个温度系统将关机。

再接下来是创建 mlnvalidChargerObserver 对象,这个对象是一个用于监听 UEvent 事件的对象,这里主要用于监听设备插入了无效充电器的事件,事件发生时将会调用该对象的 onUEvent设备文件方法,如下所示:

final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
                    synchronized (mLock) 
                        if (mInvalidCharger != invalidCharger) 
                            mInvalidCharger = invalidCharger;
                        
                    

分析:onEvent()方法中将设置 BatterService的成员变量的值invalidCharger 。这样外界通过BatterService 就能查询充电器是否匹配。最后构造方法中创建了一个BatteryListener对象,并把它加入batterypropreg服务的回调接口中。

       先看看BatteryListener类的定义,BatteryListener 是一个Binder 服务类,因此,batterypropreg 服务能通过它传递回数据。这里传递回来的数据类型是BatteryProperties,定义如下:

/frameworks/base/services/core/java/com/android/server/BatteryService.java
		
		   private final class BatteryListener extends IBatteryPropertiesListener.Stub 
        @Override public void batteryPropertiesChanged(BatteryProperties props) 
            final long identity = Binder.clearCallingIdentity();
            try 
                BatteryService.this.update(props);
             finally 
                Binder.restoreCallingIdentity(identity);
            
       
    

        BatteryListener是一个Binder服务类,因此,batterpropreg服务能通过它传递回数据。这里传递回来的数据BatteryProperties,定义如下:

package android.os;

/**
 * @hide
 */
public class BatteryProperties implements Parcelable 
    public boolean chargerAcOnline;        //正在用AC充电器充电                 
    public boolean chargerUsbOnline;        //正在用USB充电器充电
    public boolean chargerWirelessOnline;    //正在用无线充电器充电 
    public int maxChargingCurrent;          
    public int maxChargingVoltage;
    public int batteryStatus;                // 电池状态值                  
    public int batteryHealth;                //电池的健康度
    public boolean batteryPresent;           //设备是否在使用电池供电
    public int batteryLevel;                  //电量级别
    public int batteryVoltage;               //电压值
    public int batteryTemperature;           //电池温度
    public int batteryFullCharge;            //电量值 
    public int batteryChargeCounter;         //充电的时间
    public String batteryTechnology;         //电池的制造商信息

    public BatteryProperties() 
    

    public void set(BatteryProperties other) 
        chargerAcOnline = other.chargerAcOnline;
        chargerUsbOnline = other.chargerUsbOnline;
        chargerWirelessOnline = other.chargerWirelessOnline;
        maxChargingCurrent = other.maxChargingCurrent;
        maxChargingVoltage = other.maxChargingVoltage;
        batteryStatus = other.batteryStatus;
        batteryHealth = other.batteryHealth;
        batteryPresent = other.batteryPresent;
        batteryLevel = other.batteryLevel;
        batteryVoltage = other.batteryVoltage;
        batteryTemperature = other.batteryTemperature;
        batteryFullCharge = other.batteryFullCharge;
        batteryChargeCounter = other.batteryChargeCounter;
        batteryTechnology = other.batteryTechnology;
    

    /*
     * Parcel read/write code must be kept in sync with
     * frameworks/native/services/batteryservice/BatteryProperties.cpp
     */

    private BatteryProperties(Parcel p) 
        chargerAcOnline = p.readInt() == 1 ? true : false;
        chargerUsbOnline = p.readInt() == 1 ? true : false;
        chargerWirelessOnline = p.readInt() == 1 ? true : false;
        maxChargingCurrent = p.readInt();
        maxChargingVoltage = p.readInt();
        batteryStatus = p.readInt();
        batteryHealth = p.readInt();
        batteryPresent = p.readInt() == 1 ? true : false;
        batteryLevel = p.readInt();
        batteryVoltage = p.readInt();
        batteryTemperature = p.readInt();
        batteryFullCharge = p.readInt();
        batteryChargeCounter = p.readInt();
        batteryTechnology = p.readString();
    

    public void writeToParcel(Parcel p, int flags) 
        p.writeInt(chargerAcOnline ? 1 : 0);
        p.writeInt(chargerUsbOnline ? 1 : 0);
        p.writeInt(chargerWirelessOnline ? 1 : 0);
        p.writeInt(maxChargingCurrent);
        p.writeInt(maxChargingVoltage);
        p.writeInt(batteryStatus);
        p.writeInt(batteryHealth);
        p.writeInt(batteryPresent ? 1 : 0);
        p.writeInt(batteryLevel);
        p.writeInt(batteryVoltage);
        p.writeInt(batteryTemperature);
        p.writeInt(batteryFullCharge);
        p.writeInt(batteryChargeCounter);
        p.writeString(batteryTechnology);
    

    public static final Parcelable.Creator<BatteryProperties> CREATOR
        = new Parcelable.Creator<BatteryProperties>() 
        public BatteryProperties createFromParcel(Parcel p) 
            return new BatteryProperties(p);
        

        public BatteryProperties[] newArray(int size) 
            return new BatteryProperties[size];
        
    ;

    public int describeContents() 
        return 0;
    

Healthd守护进程 

      BatteryService中使用的batterypropreg 服务位于healthd守护进程中,healthd在init.rc中的定义如下:

/system/core/rootdir/init.rc
	
	service healthd /system/bin/healthd
    class core
    critical
    group root system wakelock

这里定义了一个服务 系统初始化时就会启动它。healthd的代码在system/core/healthd下,看一下healthd模块的main()函数,代码如下:

/system/core/healthd/healthd_common.cpp
	
	int healthd_main() 
    int ret;

    klog_set_level(KLOG_LEVEL);

    if (!healthd_mode_ops) 
        KLOG_ERROR("healthd ops not set, exiting\\n");
        exit(1);
    

    ret = healthd_init();
    if (ret) 
        KLOG_ERROR("Initialization failed, exiting\\n");
        exit(2);
    

    healthd_mainloop(); //进入主循环 
    KLOG_ERROR("Main loop terminated, exiting\\n");
    return 3;


static int healthd_init() 
    epollfd = epoll_create(MAX_EPOLL_EVENTS);
    if (epollfd == -1) 
        KLOG_ERROR(LOG_TAG,
                   "epoll_create failed; errno=%d\\n",
                   errno);
        return -1;
    
       
    healthd_board_init(&healthd_config);
    healthd_mode_ops->init(&healthd_config);
    wakealarm_init();
    uevent_init();
    gBatteryMonitor = new BatteryMonitor();
    gBatteryMonitor->init(&healthd_config);
    return 0;

分析:healthd_init()函数中调用 wakealarm_ini()的目的是创建一个定时器的文件句柄,代码如下:

static void wakealarm_init(void) 
    wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
    if (wakealarm_fd == -1) 
        KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\\n");
        return;
    

    if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
        KLOG_ERROR(LOG_TAG,
                   "Registration of wakealarm event failed\\n");

    wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);

分析: wakealarm initO函数调用timerfd_createO创建了一个定时器的文件句柄,并保存在全局变量
wakealarm_fd中,然后通过wakealarm_set_interval()来设置定时器的超时时间. 这个定时器的作用我们看看healthd_mainloopO的代码就能了解:

static void healthd_mainloop(void) 
    int nevents = 0;
    while (1) 
        struct epoll_event events[eventct];
        int timeout = awake_poll_interval;
        int mode_timeout;

        /* Don't wait for first timer timeout to run periodic chores */
        if (!nevents)
            periodic_chores();

        healthd_mode_ops->heartbeat();

        mode_timeout = healthd_mode_ops->preparetowait();
        if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
            timeout = mode_timeout;
        nevents = epoll_wait(epollfd, events, eventct, timeout);
        if (nevents == -1) 
            if (errno == EINTR)
                continue;
            KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\\n");
            break;
        

        for (int n = 0; n < nevents; ++n) 
            if (events[n].data.ptr)
                (*(void (*)(int))events[n].data.ptr)(events[n].events);
        
    

    return;

分析: healthd_mainloop中通过epoll来监听uevent_fd,wakealarm_fd和binder_fd 3个句柄的状态,
一旦有数据到来就会epoll_wait()的调用中返回。当监听到定时器的时间后,定时器事件的处理函数wakealarm_event()将会被调用,代码如下:

 static void wakealarm_event(uint32_t /*epevents*/) 
    unsigned long long wakeups;
    if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) 
        KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\\n");
        return;
    
    periodic_chores();

wakealarm_event清空了wakealarm_fd中的数据后,调用了函数periodic_chores(),代码如下:

void healthd_battery_update(void) 
    // Fast wake interval when on charger (watch for overheat);
    // slow wake interval when on battery (watch for drained battery).

   int new_wake_interval = gBatteryMonitor->update() ?
       healthd_config.periodic_chores_interval_fast :
           healthd_config.periodic_chores_interval_slow;

    if (new_wake_interval != wakealarm_wake_interval)
            wakealarm_set_interval(new_wake_interval);

    // During awake periods poll at fast rate.  If wake alarm is set at fast
    // rate then just use the alarm; if wake alarm is set at slow rate then
    // poll at fast rate while awake and let alarm wake up at slow rate when
    // asleep.

    if (healthd_config.periodic_chores_interval_fast == -1)
        awake_poll_interval = -1;
    else
        awake_poll_interval =
            new_wake_interval == healthd_config.periodic_chores_interval_fast ?
                -1 : healthd_config.periodic_chores_interval_fast * 1000;


void healthd_dump_battery_state(int fd) 
    gBatteryMonitor->dumpState(fd);
    fsync(fd);


static void periodic_chores() 
    healthd_battery_update();

分析: battery_update()函数中调用gBatteryMonitor的update()函数来读取电池的状态,然后根据返回值来决定是否更新定时器的时间周期。这样在定时器的作用下,系统中电池的状态会持续更新。

BatteryMonitor类

          BatteryMonitor的作用是从设备文件中读取电池的各种参数并返回给上层应用。在healthd的mainO函数中会创建BatteryMonitor对象,并调用它的initO函数,代码如下:

 /system/core/healthd/healthd_common.cpp

  static int healthd_init() 
    epollfd = epoll_create(MAX_EPOLL_EVENTS);
    if (epollfd == -1) 
        KLOG_ERROR(LOG_TAG,
                   "epoll_create failed; errno=%d\\n",
                   errno);
        return -1;
    

    healthd_board_init(&healthd_config);
    healthd_mode_ops->init(&healthd_config);
    wakealarm_init();
    uevent_init();
    gBatteryMonitor = new BatteryMonitor();
    gBatteryMonitor->init(&healthd_config);
    return 0;

/system/core/healthd/BatteryMonitor.cpp

void BatteryMonitor::init(struct healthd_config *hc) 
    String8 path;
    char pval[PROPERTY_VALUE_MAX];

    mHealthdConfig = hc;
    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);
    if (dir == NULL) 
        KLOG_ERROR(LOG_TAG, "Could not open %s\\n", POWER_SUPPLY_SYSFS_PATH);
     else 
        struct dirent* entry;

        while ((entry = readdir(dir.get()))) 
            const char* name = entry->d_name;

            if (!strcmp(name, ".") || !strcmp(name, ".."))
                continue;

            // Look for "type" file in each subdirectory
            path.clear();
            path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
            switch(readPowerSupplyType(path)) 
            case ANDROID_POWER_SUPPLY_TYPE_AC:
            case ANDROID_POWER_SUPPLY_TYPE_USB:
            case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
                path.clear();
                path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
                if (access(path.string(), R_OK) == 0)
                    mChargerNames.add(String8(name));
                break;

            case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
                mBatteryDevicePresent = true;

                if (mHealthdConfig->batteryStatusPath.isEmpty()) 
                    path.clear();
                    path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryStatusPath = path;
                

                if (mHealthdConfig->batteryHealthPath.isEmpty()) 
                    path.clear();
                    path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryHealthPath = path;
                

                if (mHealthdConfig->batteryPresentPath.isEmpty()) 
                    path.clear();
                    path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryPresentPath = path;
                

                if (mHealthdConfig->batteryCapacityPath.isEmpty()) 
                    path.clear();
                    path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryCapacityPath = path;
                

                if (mHealthdConfig->batteryVoltagePath.isEmpty()) 
                    path.clear();
                    path.appendFormat("%s/%s/voltage_now",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0) 
                        mHealthdConfig->batteryVoltagePath = path;
                     else 
                        path.clear();
                        path.appendFormat("%s/%s/batt_vol",
                                          POWER_SUPPLY_SYSFS_PATH, name);
                        if (access(path, R_OK) == 0)
                            mHealthdConfig->batteryVoltagePath = path;
                    
                

                if (mHealthdConfig->batteryFullChargePath.isEmpty()) 
                    path.clear();
                    path.appendFormat("%s/%s/charge_full",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryFullChargePath = path;
                

                if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) 
                    path.clear();
                    path.appendFormat("%s/%s/current_now",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryCurrentNowPath = path;
                

                if (mHealthdConfig->batteryCycleCountPath.isEmpty()) 
                    path.clear();
                    path.appendFormat("%s/%s/cycle_count",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryCycleCountPath = path;
                

                if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) 
                    path.clear();
                    path.appendFormat("%s/%s/current_avg",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryCurrentAvgPath = path;
                

                if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) 
                    path.clear();
                    path.appendFormat("%s/%s/charge_counter",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryChargeCounterPath = path;
                

                if (mHealthdConfig->batteryTemperaturePath.isEmpty()) 
                    path.clear();
                    path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path, R_OK) == 0) 
                        mHealthdConfig->batteryTemperaturePath = path;
                     else 
                        path.clear();
                        path.appendFormat("%s/%s/batt_temp",
                                          POWER_SUPPLY_SYSFS_PATH, name);
                        if (access(path, R_OK) == 0)
                            mHealthdConfig->batteryTemperaturePath = path;
                    
                

                if (mHealthdConfig->batteryTechnologyPath.isEmpty()) 
                    path.clear();
                    path.appendFormat("%s/%s/technology",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryTechnologyPath = path;
                

                break;

            case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
                break;
            
        
    
  ...
  
;

分析:init()函数首先打开/sys/class/power_supply,该目录下包含了一些子目录,其中battery 目录下存放的是电池信息,这个目录下有很多文件,每个文件对应电池的一种属性,文件的内容就是各个属性的当前值。usb 目录表示USB充电器的信息,里面的online 文件内容为1表示正在用 usb充电。同样 wireless 目录保存的是无线充电器的信息。 ups、 usb_dcp、usb_cdp 和usb_aca 目录则分别表示不同类型的AC充电器的信息。init()函数主要功能是生成所有这些文件的文件名并保存到成员变量中,方便以后读取。完成这个工作后,initO函数创建了BatteryPropertiesRegistrar对象,这个对象是一个Binder服务对象,它就是在BatteryService中使用的batterypropreg服务。

以上是关于Android源码笔记--电量的主要内容,如果未能解决你的问题,请参考以下文章

Android源码笔记--电量

Android源码笔记--电量

Android源码笔记--电量

ChatGPT解答:安卓APP耗电量测试方案和源码,用Android代码实现

锂电池电压电量关系

Android耗电量一体化监控: Battery Historian + APM