概述
Healthd是Android4.4之后提出來的一種中介模型,該模型向下監(jiān)聽來自底層的電池事件,向上傳遞電池數(shù)據(jù)信息給Framework層的BatteryService用以計算電池電量相關(guān)狀態(tài)信息,BatteryServcie通過傳遞來的數(shù)據(jù)來計算電池電量顯示,剩余電量,電量級別等信息,如果收到過溫報警或者嚴重低電報警等信息,系統(tǒng)會直接關(guān)機,保護硬件。
主模塊處理流程
Healthd模塊代碼是在system/core/healthd/,其模塊入口在healthd的main函數(shù),函數(shù)代碼如下:
int main(int argc, char **argv) { int ch; int ret; klog_set_level(KLOG_LEVEL); healthd_mode_ops = &android_ops; if (!strcmp(basename(argv[0]), "charger")) { healthd_mode_ops = &charger_ops; } else { while ((ch = getopt(argc, argv, "cr")) != -1) { switch (ch) { case 'c': healthd_mode_ops = &charger_ops; break; case 'r': healthd_mode_ops = &recovery_ops; break; case '?': default: KLOG_ERROR(LOG_TAG, "Unrecognized healthd option: %cn", optopt); exit(1); } } } ret = healthd_init(); if (ret) { KLOG_ERROR("Initialization failed, exitingn"); exit(2); } healthd_mainloop(); KLOG_ERROR("Main loop terminated, exitingn"); return 3; }
可以看出Main函數(shù)并不長,但是其作用確實巨大的,main函數(shù)起著一個統(tǒng)籌兼顧的作用,其他各個模塊函數(shù)去做一些具體相應(yīng)的工作,最后匯總到main函數(shù)中被調(diào)用。
代碼中開始便是解析參數(shù),healthd_mode_ops是一個關(guān)于充電狀態(tài)結(jié)構(gòu)體變量,結(jié)構(gòu)體變量里的參數(shù)是函數(shù)指針,在初始化時指向各個不同的操作函數(shù),當開機充電時變量賦值為&android_ops,關(guān)機充電時候變量賦值為&charger_ops。
在ret = healthd_init();中進行一些初始化工作。
static int healthd_init() { epollfd = epoll_create(MAX_EPOLL_EVENTS); if (epollfd == -1) { KLOG_ERROR(LOG_TAG, "epoll_create failed; errno=%dn", 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; }
創(chuàng)建一個epoll的變量將其賦值給epollfd,在healthd_board_init中未作任何事便返回了。
healthd_mode_ops->init調(diào)用有兩種情況:關(guān)機情況下調(diào)用charger_ops的init函數(shù);開機情況下調(diào)用android_ops的init函數(shù),這里就開機情況來分析。android_ops的init函數(shù)指針指向healthd_mode_android_init函數(shù)
代碼如下:
void healthd_mode_android_init(struct healthd_config* /*config*/) { ProcessState::self()->setThreadPoolMaxThreadCount(0);//線程池里最大線程數(shù) IPCThreadState::self()->disableBackgroundScheduling(true);//禁用后臺調(diào)度 IPCThreadState::self()->setupPolling(&gBinderFd);// if (gBinderFd >= 0) { if (healthd_register_event(gBinderFd, binder_event)) KLOG_ERROR(LOG_TAG, "Register for binder events failedn"); } gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(); gBatteryPropertiesRegistrar->publish(); }
再來看看wakealarm_init函數(shù):
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 failedn"); return; } if (healthd_register_event(wakealarm_fd, wakealarm_event)) KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failedn"); wakealarm_set_interval(healthd_config.periodic_chores_interval_fast); }
首先創(chuàng)建一個wakealarm_fd的定時器與之對應(yīng)的文件描述符,healthd_register_event將wakealarm事件注冊到wakealarm_fd文件節(jié)點用以監(jiān)聽wakealarm事件,wakealarm_set_interval設(shè)置alarm喚醒的間隔
再看看uevent_init函數(shù):
static void uevent_init(void) { uevent_fd = uevent_open_socket(64*1024, true); if (uevent_fd < 0) { KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failedn"); return; } fcntl(uevent_fd, F_SETFL, O_NONBLOCK); if (healthd_register_event(uevent_fd, uevent_event)) KLOG_ERROR(LOG_TAG, "register for uevent events failedn"); }
創(chuàng)建并打開一個64k的socket文件描述符uevent_fd,設(shè)置文件狀態(tài)標志為非阻塞模,將uevent事件注冊到uevent_fd文件節(jié)點用以監(jiān)聽uevent事件。
我們可以看到android利用epoll監(jiān)聽了三個文件節(jié)點的改變事件,分別是:通過gBinderfd監(jiān)聽線程Binder通信事件;通過wakealarm_fd監(jiān)聽wakealarm事件;通過uevent_fd監(jiān)聽wakealarm事件。至于如何監(jiān)聽后面做詳細分析
在healthd_init中最后創(chuàng)建BatteryMonitor的對象,并將其初始化。BatteryMonitor主要接受healthd傳來的數(shù)據(jù),做電池狀態(tài)的計算并更新。
我們可以看到在BatterMonitor中的init函數(shù)中有以下語句:
DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH); struct dirent* entry; 。。。。。。。 while ((entry = readdir(dir))) { const char* name = entry->d_name; 。。。。。。 }
POWER_SUPPLY_SYSFS_PATH定義為"/sys/class/power_supply",在init函數(shù)中打開系統(tǒng)該文件夾,然后一一讀取該文件夾下的文件內(nèi)容,在while循環(huán)中判斷該文件夾下各個文件節(jié)點的內(nèi)容,并將其初始化給相關(guān)的參數(shù).
至此,healthd_init函數(shù)就分析完了,其主要工作就是:創(chuàng)建了三個文件節(jié)點用來監(jiān)聽相應(yīng)的三種事件改變;創(chuàng)建BatteryMonitor對象,并通過讀取/sys/class/power_supply將其初始化。
Healthd_init走完之后,接著就是調(diào)用healthd_mainloop函數(shù),該函數(shù)維持了一個死循環(huán),代碼如下:
static void healthd_mainloop(void) { while (1) { struct epoll_event events[eventct]; int nevents; int timeout = awake_poll_interval; int mode_timeout; 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 failedn"); break; } for (int n = 0; n < nevents; ++n) { if (events[n].data.ptr) (*(void (*)(int))events[n].data.ptr)(events[n].events); } if (!nevents) periodic_chores(); healthd_mode_ops->heartbeat(); } return; }
Healthd_mainloop中維持了一個死循環(huán),死循環(huán)中變量nevents 表示從epollfd中輪循中監(jiān)聽得到的事件數(shù)目,這里介紹一下輪詢機制中重要函數(shù)epoll_waite().
epoll_wait運行的道理是:等侍注冊在epfd上的socket fd的事務(wù)的產(chǎn)生,若是產(chǎn)生則將產(chǎn)生的sokct fd和事務(wù)類型放入到events數(shù)組中。且timeout如果為-1則為阻塞式,timeowout為0則表示非阻塞式。可以看到代碼中timeout為-1,故為阻塞式輪詢,當epollfd上有事件發(fā)生,則會走到下面的處理邏輯。事件處理主要在for循環(huán)中:
在periodic_chores()中調(diào)用到healthd_battery_update()更新電池狀態(tài)。
void healthd_battery_update(void) { 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); 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; }
可以看出該函數(shù)并不長,new_wake_interval表示新的wakealarm喚醒間隔,通過調(diào)用BatteryMonitor的update函數(shù)(后面詳細分析如何更新),其返回值為是否處于充電狀態(tài),當處于充電狀態(tài),則喚醒間隔為healthd_config.periodic_chores_interval_fast(短間隔),當不再充電狀態(tài)時喚醒間隔為healthd_config.periodic_chores_interval_slow(長間隔)
當新的間隔變量new_wake_interval與舊的變量wakealarm_wake_interval不一樣,則將新的喚醒間隔設(shè)置成wakealarm的喚醒間隔;
awake_poll_internal作為下一次epoll_waite的timeout參數(shù),在這里將其更新,在充電狀態(tài)下awake_poll_internal為-1,沒有充電的狀態(tài)下awake_poll_internal為60000ms
healthd主流程都是在main函數(shù)中處理,至此main已經(jīng)分析完成,其簡要流程圖如下

Healthd處理邏輯
初始化處理
前面將healthd模塊中main函數(shù)分析完了,其主要工作流程有個大概的了解,但是其詳細處理邏輯并未做分析,在此之后,對Healthd的初始化,事件處理,狀態(tài)更新將做一個詳細的分析。
前面已經(jīng)說過在healthd_init中創(chuàng)建了三個文件節(jié)點gBinderfd,uevent_fd,wakealarm_fd,并用以注冊監(jiān)聽三種事件,注冊監(jiān)聽都是通過healthd_register_event函數(shù)實現(xiàn)的。
healthd_register_event(gBinderFd, binder_event);
healthd_register_event(wakealarm_fd, wakealarm_event);
healthd_register_event(uevent_fd, uevent_event);
其healthd_register_event實現(xiàn)代碼如下:
int healthd_register_event(int fd, void (*handler)(uint32_t)) { struct epoll_event ev; ev.events = EPOLLIN | EPOLLWAKEUP; ev.data.ptr = (void *)handler; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { KLOG_ERROR(LOG_TAG, "epoll_ctl failed; errno=%dn", errno); return -1; } eventct++; return 0; }
函數(shù)將相應(yīng)的文件節(jié)點事件賦值為函數(shù)的第二個形參,也就是說相應(yīng)的gBinderfd的事件處理函數(shù)為binder_event函數(shù),同理wakealarm_fd,ueven_fd的事件事件處理分別為wakealarm_event,uevent_event函數(shù)。然后將其三個文件節(jié)點加入到epollfd中。
事件獲取與處理
Healthd中維持了一個阻塞式的死循環(huán)healthd_mainloop,在該函數(shù)中提供阻塞式的監(jiān)聽已發(fā)送的事件函數(shù)epoll_wait(),healthd_mainloop中有如下代碼
nevents = epoll_wait(epollfd, events, eventct, timeout); for (int n = 0; n < nevents; ++n) { if (events[n].data.ptr) (*(void (*)(int))events[n].data.ptr)(events[n].events); }
當epoll_waite接受到gBinderfd,wakealarm_fd,uevent_fd其中的事件,便會將監(jiān)聽到的事件加入到event數(shù)組中。在for循環(huán)中做處理,for循環(huán)中代碼看起來非常難懂,其實if判斷的便是event有沒有相應(yīng)的處理函數(shù),在前面注冊事件時候已經(jīng)提到,三種句柄上的事件都有對應(yīng)的處理函數(shù),也就是當收到gBinderfd上的事件,便用binder_event函數(shù)處理,當收到uevent_fd上的事件便用uevent_event處理,當收到wakealarm_fd上的事件便用wakealarm_event處理。
這里以較為重要的uevent_event事件處理為例:
#define UEVENT_MSG_LEN 2048 static void uevent_event(uint32_t /*epevents*/) { char msg[UEVENT_MSG_LEN+2]; char *cp; int n; n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN); if (n <= 0) return; if (n >= UEVENT_MSG_LEN) /* overflow -- discard */ return; msg[n] = ''; msg[n+1] = ''; cp = msg; while (*cp) { if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) { healthd_battery_update(); break; } /* advance to after the next */ while (*cp++) ; } }
處理函數(shù)首先從uevent_fd 獲取事件數(shù)目,然后循環(huán)判斷是否是來自與power_supply目錄下的事件,如果是,則調(diào)用到healthd_battery_update中去更新電池狀態(tài)。
更新電池狀態(tài)
當收到事件,做一些判斷工作便需要更新電池狀態(tài),其更新函數(shù)為healthd.cpp下的healthd_battery_update函數(shù),但是主要更新并不在heathd中完成的,而是在BatteryMonitor中的update函數(shù),其代碼較多,分段分析:
props.chargerAcOnline = false; props.chargerUsbOnline = false; props.chargerWirelessOnline = false; props.batteryStatus = BATTERY_STATUS_UNKNOWN; props.batteryHealth = BATTERY_HEALTH_UNKNOWN; if (!mHealthdConfig->batteryPresentPath.isEmpty()) props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath); else props.batteryPresent = mBatteryDevicePresent; props.batteryLevel = mBatteryFixedCapacity ? mBatteryFixedCapacity : getIntField(mHealthdConfig->batteryCapacityPath); props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000; props.batteryTemperature = mBatteryFixedTemperature ? mBatteryFixedTemperature : getIntField(mHealthdConfig->batteryTemperaturePath); const int SIZE = 128; char buf[SIZE]; String8 btech; if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0) props.batteryStatus = getBatteryStatus(buf); if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0) props.batteryHealth = getBatteryHealth(buf); if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0) props.batteryTechnology = String8(buf);
在init函數(shù)中將healthd_config 對象傳入,并且將里面的成員的一些地址信息去初始化保存起來。主要是保存一些地址信息,以及充電方式。在BatteryMonitor初始化中,heathd_config傳入init函數(shù)中,賦值為mHealthdConfig,上面一段主要是讀取/sys/class/power_supply下的文件節(jié)點信息初更新電池數(shù)據(jù)屬性值,
path.AppendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].string()); if (readFromFile(path, buf, SIZE) > 0) { if (buf[0] != '0') { path.clear(); path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].string()); switch(readPowerSupplyType(path)) { case ANDROID_POWER_SUPPLY_TYPE_AC: props.chargerAcOnline = true; break; case ANDROID_POWER_SUPPLY_TYPE_USB: props.chargerUsbOnline = true; break; case ANDROID_POWER_SUPPLY_TYPE_WIRELESS: props.chargerWirelessOnline = true; break; default: KLOG_WARNING(LOG_TAG, "%s: Unknown power supply typen", mChargerNames[i].string()); } } }
將電池當前的電量級別,電壓,溫度,健康狀況,電池狀態(tài)以及充放電倍率存入dmesgline變量中,在后面會將電池充電類型,電池使用時間都以字符串存入dmesgline變量中,然后:
KLOG_WARNING(LOG_TAG, "%sn", dmesgline);//向log記錄電池當前各種狀態(tài)信息 } healthd_mode_ops->battery_update(&props);//更新電池 return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline;//返回是否在充電狀態(tài)
整個update函數(shù)做完更新數(shù)據(jù),記錄數(shù)據(jù)到log之后,然后調(diào)用到BatteryPropertiesRegistrar的update函數(shù)繼續(xù)更新電池狀態(tài),最后返回值為是否處于充電狀態(tài)。
BatteryPropertiesRegistrar的update函數(shù)未作任何操作調(diào)用Healthd_mode_android.cpp中的healthd_mode_android_battery_update函數(shù),我們可以看看該函數(shù)
void healthd_mode_android_battery_update( struct android::BatteryProperties *props) { if (gBatteryPropertiesRegistrar != NULL) gBatteryPropertiesRegistrar->notifyListeners(*props); return; }
這里這里直接調(diào)用到BatteryPropertiesRegistrar的notifyListeners去通知props改變了,props是什么呢?props是定義的一個BatteryProperties屬性集,里面的成員變量包含了所有的電池狀態(tài)信息,在update開始便通過讀取各個文件節(jié)點的實時數(shù)據(jù)更新電池屬性props,更新完成后通過BatteryPropertiesRegistrar通知其屬性監(jiān)聽者去更新狀態(tài),但是誰是監(jiān)聽呢?
我們可以看到framework層中的BatteryService.JAVA的onStart函數(shù)中有如下代碼:
public void onStart() { IBinder b = ServiceManager.getService("batteryproperties"); final IBatteryPropertiesRegistrar batteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(b); try { batteryPropertiesRegistrar.registerListener(new BatteryListener()); } catch (RemoteException e) { // Should never happen. }
我們在初始化的時候已經(jīng)提到過,當healthd初始化時候會創(chuàng)建BatteryPropertiesRegistrar的對象并將其publish注冊到系統(tǒng)服務(wù)中,注冊服務(wù)的語句如下:
defaultServiceManager()->addService(String16("batteryproperties"), this);
所以BatteryService在這里獲取該服務(wù),并以此注冊其監(jiān)聽器為BatteryListener(),該監(jiān)聽器監(jiān)聽到BatteryProperties改變便會調(diào)用到BatteryService的update函數(shù),去做電池電量相關(guān)計算以及顯示。
至此更新操作基本分析完成,其簡要流程如下圖所示

總結(jié)
Healthd是framework層傳遞來自底層電池事件信息并調(diào)用相關(guān)模塊更新電池狀態(tài)的一個中間層,其向下監(jiān)聽來自底層PMU驅(qū)動上報的uevent電池事件,向上調(diào)用BatteryService去計算電池,電量,使用等相關(guān)信息,它通過一個阻塞式的死循環(huán)不斷監(jiān)聽底層三個文件節(jié)點上的事件信息,當監(jiān)聽到事件便調(diào)用到BatteryMonitor執(zhí)行更新操作,通過BatteryService.java中注冊監(jiān)聽電池屬性改變的函數(shù),當電池屬性信息發(fā)生改變,即回調(diào)到BatteryService中做更新操作,更新完成一次電池事件的上報到更新整個流程就完成;總之Healthd是連接Battery模塊framework中java層與HAL層交互的主要通道。