日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

概述

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)分析完成,其簡要流程圖如下

android6.0系統(tǒng)Healthd深入分析

 

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)計算以及顯示。

至此更新操作基本分析完成,其簡要流程如下圖所示

android6.0系統(tǒng)Healthd深入分析

 

總結(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層交互的主要通道。

分享到:
標簽:android
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定