Android電池管理系統(tǒng)

Android電池管理系統(tǒng)總體實現(xiàn):
電池管理在Android系統(tǒng)中的主要作用是檢測我們的電池狀態(tài),剩余電量實時更新,高溫報警,低電量關(guān)機等。Android的電池管理模塊,從Android的應(yīng)用層到底層內(nèi)核分為了4層來理解,從上到下依次為,應(yīng)用層,framwork層,本地框架層,內(nèi)核驅(qū)動層。

一、Android 電池服務(wù)

Android電池服務(wù)BatteryService,用來監(jiān)聽內(nèi)核上報的電池事件,并將最新的電池數(shù)據(jù)上報給系統(tǒng),系統(tǒng)收到新數(shù)據(jù)后會去更新電池顯示狀態(tài)、剩余電量等信息。如果收到過溫報警和低電報警,系統(tǒng)會自動觸發(fā)關(guān)機流程,保護電池和機器不受到危害。

Android電池服務(wù)的源碼結(jié)構(gòu) :

frameworks/base/services/java/com/android/server/
       ├── SystemServer.java
                創(chuàng)建BatteryService、PowerManagerService、ActivityManagerService
       frameworks/base/services/core/java/com/android/server/
       ├── BatteryService.java
                監(jiān)聽底層上報的battery事件,廣播電池發(fā)生改變的消息 (廣播Intent.ACTION_BATTERY_CHANGED)
       frameworks/base/services/core/java/com/android/server/am/
       ├── ActivityManagerService.java
                創(chuàng)建BatteryStatsService
       ├── BatteryStatsService.java
                統(tǒng)計和記錄電池參數(shù)的信息
       frameworks/base/services/core/java/com/android/server/power/
       ├── PowerManagerService.java
                監(jiān)聽電池發(fā)生變化的廣播消息,并調(diào)節(jié)系統(tǒng)的電源狀態(tài),例如亮屏
       frameworks/base/core/java/com/android/internal/os/
       ├── BatteryStatsImpl.java
                統(tǒng)計和記錄電池參數(shù)的信息,并通知其他模塊

二、Healthd

healthd是android4.4之后提出來的一種中介模型,安卓源碼路徑下system/core/healthd, 主要是通過binder機制去調(diào)用healthd向下監(jiān)聽來自底層的電池事件,向上傳遞電池數(shù)據(jù)信息給Framework層的BatteryService用來計算電池電量相關(guān)信息,BatteryService通過傳遞來的數(shù)據(jù)來計算電池電量等信息,因此healthd在電池管理系統(tǒng)中起著承上啟下的作用。

主要是通過BatteryMonitor.cpp中的bool BatteryMonitor::update(void)函數(shù)上報信息,其中,內(nèi)核首先會更新數(shù)據(jù)到/sys/class/power_supply/battery節(jié)點下各個屬性。

Healthd的源碼結(jié)構(gòu):

System/core/healthd/
       ├── healthd.cpp
                創(chuàng)建uevent socket,監(jiān)聽內(nèi)核上報的內(nèi)核事件
       ├── BatteryMonitor.cpp
                初始化本地電池數(shù)據(jù)結(jié)構(gòu),將power_supply路徑下屬性節(jié)點路徑填充進去,
       ├── BatteryMonitor.h
       ├── BatteryPropertiesRegistrar.cpp
                創(chuàng)建電池屬性監(jiān)聽器,并將其注冊到Android的系統(tǒng)服務(wù)中
       ├── BatteryPropertiesRegistrar.h

health模塊的代碼位置位于/system/core/healthd/,其入口在Healthd.cpp中的main函數(shù)中:

healthd_mode_ops = &android_ops;  //開機充電時初始化結(jié)構(gòu)體
healthd_mode_ops = &charger_ops;  //關(guān)機充電時初始化結(jié)構(gòu)體

healthd_mode_ops是一個充電狀態(tài)的結(jié)構(gòu)體,正常開機情況下會 將android_ops結(jié)構(gòu)體賦值給healthd_mode_ops ,如果在關(guān)機情況下會將 charget_ops結(jié)構(gòu)體賦值給healthd_mode_opos,就是關(guān)機充電的使用。

然后在healthd_init中,主要做一些初始化工作,并且創(chuàng)建了一個epoll,主要用于將文件指針掛在工作隊列中,用于輪詢查看驅(qū)動層是否發(fā)來信息,然后調(diào)用uevent_init()對uevent進行注冊,應(yīng)為從上篇文章我們明白,電池驅(qū)動是通過uevent與health進行通信的,uevent通信本質(zhì)上就是socket通信。

epollfd = epoll_create(MAX_EPOLL_EVENTS);

初始化之后,執(zhí)行healthd_mainloop(),開啟了一個無線循環(huán),監(jiān)聽是否有uevent事件到來,如果在for循環(huán)中檢測到uevent事件到來的時候,會直接調(diào)用 uevent_event函數(shù)

在uevent_event函數(shù)中,會判斷SSUBSYSTEM是否等于POWER_SUPPLY_SUBSYSTEM,如果是的話,說明是電池驅(qū)動傳過來的信息,會執(zhí)行healthd_battery_update()函數(shù)。在該函數(shù)中會直接調(diào)用BatteryMonitor中的update()函數(shù),對電池的各個屬性進行更新,并在最后調(diào)用在前面注冊的監(jiān)聽進行上報。

在update()函數(shù)中調(diào)用 healthd_mode_ops->battery_update(&props) 進行,其中battery_update(&props)為在health.cpp中初始化的結(jié)構(gòu)體,最后返回當(dāng)前的充電狀態(tài)。

battery_update()調(diào)用的函數(shù)如下,調(diào)用注冊的監(jiān)聽進行上報:

     void healthd_mode_android_battery_update(
   struct android::BatteryProperties *props) {
   if (gBatteryPropertiesRegistrar != NULL)
       gBatteryPropertiesRegistrar->notifyListeners(*props); //調(diào)用監(jiān)聽并傳入屬性值
   return;
   }

接著gBatteryPropertiesRegistrar 這個監(jiān)聽就會回調(diào)在framwork層實現(xiàn)的函數(shù),然后通過Binder機制向上層通信

三、驅(qū)動

驅(qū)動部分大概流程是這樣的:
Android內(nèi)核中的電池驅(qū)動采取的是linux 內(nèi)核驅(qū)動中的 power_supply子系統(tǒng)框架進行上報電池狀態(tài)。power_supply主要通過sys文件系統(tǒng)向用戶層提供讀取電池狀態(tài)的接口,路徑為 /sys/class/power_supply/ , 該目錄下通常會有 ac , battery, usb 三個目錄,代表給Android系統(tǒng)供電的三種能源類型,其中電池的狀態(tài)就在battery的目錄下,當(dāng)電池狀態(tài)變化的時候會通過uevent機制通知上層,然后上層通過讀取該目錄下相應(yīng)的值來動態(tài)的顯示電池狀態(tài)。

驅(qū)動的源碼結(jié)構(gòu) :

kernel/drivers/power

1、對電源(ac , battery, usb)進行初始化(這里說的是rk818):

對應(yīng)驅(qū)動文件:drivers/power/rk818_charger.c

static const struct power_supply_desc rk818_ac_desc = {
    .name           = "ac",//設(shè)備名稱
    .type           = POWER_SUPPLY_TYPE_MAINS,//類型
    .properties     = rk818_ac_props,//屬性
    .num_properties = ARRAY_SIZE(rk818_ac_props),//屬性數(shù)目
    .get_property   = rk818_cg_ac_get_property,//得到屬性的函數(shù)
 };

 static const struct power_supply_desc rk818_usb_desc = {
     .name           = "usb",
     .type           = POWER_SUPPLY_TYPE_USB,
     .properties     = rk818_usb_props,
     .num_properties = ARRAY_SIZE(rk818_usb_props),
     .get_property   = rk818_cg_usb_get_property,
};

對應(yīng)驅(qū)動文件:drivers/power/rk818_battery.c

static const struct power_supply_desc rk818_bat_desc = {
    .name           = "battery",
    .type           = POWER_SUPPLY_TYPE_BATTERY,
    .properties     = rk818_bat_props,
    .num_properties = ARRAY_SIZE(rk818_bat_props),
    .get_property   = rk818_battery_get_property,
};

這里主要是實現(xiàn)給電源名字類型等賦初值,最主要是將get_property函數(shù)指向我們寫好的可以得到電源的屬性的函數(shù)的起始地址,以便當(dāng)內(nèi)核需要用到驅(qū)動的信息的時候進行回調(diào)。

2、通過power_supply_register(devm_power_supply_register最終也是調(diào)用power_supply_register)將所提供的電源進行注冊,即把他們的屬性寫到sys文件系統(tǒng)里,使用戶空間可以得到有關(guān)電源的信息。

cg->usb_psy = devm_power_supply_register(cg->dev, &rk818_usb_desc,
                                         &psy_cfg);

power_supply_register調(diào)用內(nèi)核提供的函數(shù)device_create()和power_supply_create_attrs來實現(xiàn)電源的注冊,這里電源類型是usb。

3、Power Supply驅(qū)動程序頭文件是kernel/include/linux/power_supply.h,注冊和注銷驅(qū)動程序的函數(shù)如下:

int power_supply_register(struct device *parent,struct power_supply *psy);
void power_supply_unregister(struct power_supply *psy);
struct power_supply {
const char *name; /*設(shè)備名稱*/
enum power_supply_type type; /* 類型 */
enum power_supply_property *properties; /* 屬性指針 */
size_t num_properties; /*屬性的數(shù)目*/
char* *supplied_to;
size_t num_supplicants;
int (*get_property)(struct power_supply *psy, /*獲得屬性*/
enum power_supply_property psp,
union power_supply_propval *val);
void (*external_power_changed)(struct power_supply *psy);
/* ...... 省略部分內(nèi)容 */

內(nèi)核主要通過get_property這個函數(shù)指針來獲得驅(qū)動中的有關(guān)電池的信息,而這個函數(shù)在內(nèi)核中只給出了聲明,我們在寫驅(qū)動的時候要自己實現(xiàn)這個函數(shù),即將自己寫的函數(shù)賦值給這個函數(shù)指針,當(dāng)內(nèi)核需要驅(qū)動中電源信息的時候就回調(diào)這個get_property函數(shù)。另外,我們寫驅(qū)動程序的時候又要給用戶提供接口,內(nèi)核中提供給用戶的接口就是sysfs,通過讀取sysfs文件系統(tǒng)中文件內(nèi)容,就可以得到電源的信息。內(nèi)核主要通過兩個文件power_supply_class.c 和power_supply_core.c,我們調(diào)用其中的函數(shù)就可以把電源(BATTERY,USB或AC)的信息展現(xiàn)給用戶,有關(guān)電源的屬性寫在/sys/class/powersupply文件夾下(此文件夾為程序運行后所生成的)。

ac和usb只創(chuàng)建了一個online屬性,上層通過判斷ac和usb的online狀態(tài)(1表示設(shè)備接入,0表示設(shè)備拔出)便可知道當(dāng)前系統(tǒng)是由什么設(shè)備在充電了;而battery則創(chuàng)建了如status、health、present、capacity、batt_vol等等和電池相關(guān)的諸多屬性,上層通過這些電池屬性uevent便可監(jiān)控電池的當(dāng)前工作狀態(tài)了。下面舉例是battery,ac和usb同理。

    static int rk818_battery_get_property(struct power_supply *psy,
                                       enum power_supply_property psp,
                                       union power_supply_propval *val)
    {
         struct rk818_battery *di = power_supply_get_drvdata(psy);

         switch (psp) {
         case POWER_SUPPLY_PROP_CURRENT_NOW:
                 val->intval = di->current_avg * 1000;/*uA*/ //獲取電池電流
                 if (di->pdata->bat_mode == MODE_VIRTUAL)
                         val->intval = VIRTUAL_CURRENT * 1000;
                 break;
          case POWER_SUPPLY_PROP_VOLTAGE_NOW:
                 val->intval = di->voltage_avg * 1000;/*uV*/ //獲取電池電壓
                 if (di->pdata->bat_mode == MODE_VIRTUAL)
                         val->intval = VIRTUAL_VOLTAGE * 1000;
                 break;
         case POWER_SUPPLY_PROP_PRESENT:
                 val->intval = is_rk818_bat_exist(di);
                 if (di->pdata->bat_mode == MODE_VIRTUAL)
                         val->intval = VIRTUAL_PRESET;
                 break;
         case POWER_SUPPLY_PROP_CAPACITY:
                 val->intval = di->dsoc; //獲取電池電量
                 if (di->pdata->bat_mode == MODE_VIRTUAL)
                         val->intval = VIRTUAL_SOC;
                 DBG("<%s>. report dsoc: %d\n", __func__, val->intval);
                 break;
         case POWER_SUPPLY_PROP_HEALTH:
                 val->intval = POWER_SUPPLY_HEALTH_GOOD;
                 break;
         case POWER_SUPPLY_PROP_TEMP:
                 val->intval = di->temperature;
                   if (di->pdata->bat_mode == MODE_VIRTUAL)
                           val->intval = VIRTUAL_TEMPERATURE;
                 break;

各能源設(shè)備屬性概況如下(adb工具cat可以查看):

    /sys/class/power_supply/ac/online AC 電源連接狀態(tài)
    /sys/class/power_supply/usb/online USB電源連接狀態(tài)
    /sys/class/power_supply/battery/status 充電狀態(tài)
    /sys/class/power_supply/battery/health 電池狀態(tài)
    /sys/class/power_supply/battery/present 使用狀態(tài)
    /sys/class/power_supply/battery/capacity 電池 level
    /sys/class/power_supply/battery/batt_vol 電池電壓
    /sys/class/power_supply/battery/batt_temp 電池溫度
    /sys/class/power_supply/battery/technology 電池技術(shù)

當(dāng)供電設(shè)備的狀態(tài)或者電量發(fā)生變化后,會調(diào)用power_supply_changed(&battery_data->battery)更新這些文件;

/Send uevent.
    power_supply_changed(&battery_data->battery);

備注:
1、Uevent機制
Uevent是內(nèi)核通知android有狀態(tài)變化的一種方法,比如USB線插入、拔出,電池電量變化等等。其本質(zhì)是內(nèi)核發(fā)送(可以通過socket)一個字符串,應(yīng)用層(android)接收并解釋該字符串,獲取相應(yīng)信息。

來自:https://www.yuque.com/yuanjiangli/quoszu/azknf5

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容