系統平臺:android6.0概述Healthd是android4.4之後提出來的一種中介模型,該模型向下監聽來自底層的電池事件,向上傳遞電池數據信息給Framework層的BatteryService用以計算電池電量相關狀態信息,BatteryServcie通過傳遞來的數據來計算電池電量顯示,剩餘 ...
系統平臺:android6.0
概述
Healthd是android4.4之後提出來的一種中介模型,該模型向下監聽來自底層的電池事件,向上傳遞電池數據信息給Framework層的BatteryService用以計算電池電量相關狀態信息,BatteryServcie通過傳遞來的數據來計算電池電量顯示,剩餘電量,電量級別等信息,如果收到過溫報警或者嚴重低電報警等信息,系統會直接關機,保護硬體。
1.主模塊處理流程
Healthd模塊代碼是在system/core/healthd/,其模塊入口在healthd的main函數,函數代碼如下:
1 int main(int argc, char **argv) { 2 int ch; 3 int ret; 4 /*代碼中開始便是解析參數,healthd_mode_ops是一個關於充電狀態結構體變數, 5 *結構體變數里的參數是函數指針,在初始化時指向各個不同的操作函數, 6 *當開機充電時變數賦值為&android_ops,關機充電時候變數賦值為&charger_ops。 7 */ 8 klog_set_level(KLOG_LEVEL); 9 healthd_mode_ops = &android_ops; //正常開機充電 10 11 //charger為該程式的可執行文件名,該行代碼主要用於區分是命令操作還是正常代碼啟動 12 if (!strcmp(basename(argv[0]), "charger")) { 13 14 healthd_mode_ops = &charger_ops; 15 } else { 16 while ((ch = getopt(argc, argv, "cr")) != -1) { 17 switch (ch) { //根據不同的參數賦與不同的操作方法 18 case 'c': 19 healthd_mode_ops = &charger_ops; 20 break; 21 case 'r': 22 healthd_mode_ops = &recovery_ops; 23 break; 24 case '?': 25 default: 26 KLOG_ERROR(LOG_TAG, "Unrecognized healthd option: %c\n", 27 optopt); 28 exit(1); 29 } 30 } 31 } 32 33 //對需要數據上報的事件進行初始化,分別是healthd_mode_ops、wakealarm和uevent三個事件 34 ret = healthd_init(); 35 36 if (ret) { 37 KLOG_ERROR("Initialization failed, exiting\n"); 38 exit(2); 39 } 40 healthd_mainloop(); 41 KLOG_ERROR("Main loop terminated, exiting\n"); 42 return 3; 43 }
1.1healthd_init( )函數分析
1 static int healthd_init() { 2 epollfd = epoll_create(MAX_EPOLL_EVENTS); //創建epoll用於事件觸發 3 if (epollfd == -1) { 4 KLOG_ERROR(LOG_TAG, 5 "epoll_create failed; errno=%d\n", 6 errno); 7 return -1; 8 } 9 healthd_board_init(&healthd_config); 10 //次處為調用android_ops->init進行初始化 11 healthd_mode_ops->init(&healthd_config); 12 //對wakealarm進行初始化,用於周期觸發事件上報數據 13 wakealarm_init(); 14 //對uevent進行初始化,用於域套節字進行數據傳輸 15 uevent_init(); 16 17 /*在healthd_init中最後創建BatteryMonitor的對象,並將其初始化。 18 BatteryMonitor主要接受healthd傳來的數據,做電池狀態的計算並更新。*/ 19 gBatteryMonitor = new BatteryMonitor(); 20 gBatteryMonitor->init(&healthd_config); 21 return 0; 22 }
創建一個epoll的變數將其賦值給epollfd,在healthd_board_init中未作任何事便返回了。
healthd_mode_ops->init調用有兩種情況:關機情況下調用charger_ops的init函數;開機情況下調用android_ops的init函數,這裡就開機情況來分析。android_ops的init函數指針指向healthd_mode_android_init函數
代碼如下:
1 void healthd_mode_android_init(struct healthd_config* /*config*/) { 2 ProcessState::self()->setThreadPoolMaxThreadCount(0);//線程池裡最大線程數 3 IPCThreadState::self()->disableBackgroundScheduling(true);//禁用後臺調度 4 IPCThreadState::self()->setupPolling(&gBinderFd); 5 if (gBinderFd >= 0) { 6 //gBinderfd加入到epoll中 7 if (healthd_register_event(gBinderFd, binder_event)) 8 9 /********healthd_register_event函數分析**************** 10 int healthd_register_event(int fd, void (*handler)(uint32_t)) { 11 struct epoll_event ev; 12 ev.events = EPOLLIN | EPOLLWAKEUP; //設置事件類型 13 ev.data.ptr = (void *)handler; //加入事件處理函數 14 //將被監聽的描述符添加到epoll 15 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { 16 KLOG_ERROR(LOG_TAG, 17 "epoll_ctl failed; errno=%d\n", errno); 18 return -1; 19 } 20 eventct++; 21 return 0; 22 } 23 */ 24 KLOG_ERROR(LOG_TAG, 25 "Register for binder events failed\n"); 26 } 27 gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(); 28 //將"batteryproperties"這個Service註冊到ServiceManager中 29 gBatteryPropertiesRegistrar->publish(); 30 }
前面三條語句做初始化工作,設置線程池最大線程數,禁用後臺調度,以及將gBinderfd加入到epoll中。healthd_register_event將binder_event事件註冊到gBinderfd文件節點用以監聽Binder事件。gBatteryPropertiesRegistrar->publish將"batteryproperties"這個Service註冊到ServiceManager中
再來看看wakealarm_init函數:
1 static void wakealarm_init(void) { 2 //首先創建一個wakealarm_fd的定時器與之對應的文件描述符 3 wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK); 4 if (wakealarm_fd == -1) { 5 KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n"); 6 return; 7 } 8 9 //healthd_register_event將wakealarm事件註冊到wakealarm_fd文件節點用以監聽wakealarm事件 10 if (healthd_register_event(wakealarm_fd, wakealarm_event)) 11 KLOG_ERROR(LOG_TAG, 12 "Registration of wakealarm event failed\n"); 13 //wakealarm_set_interval設置alarm喚醒的間隔 14 wakealarm_set_interval(healthd_config.periodic_chores_interval_fast); 15 }
再看看uevent_init函數:
1 static void uevent_init(void) { 2 //創建並打開一個64k的socket文件描述符uevent_fd 3 uevent_fd = uevent_open_socket(64*1024, true); 4 if (uevent_fd < 0) { 5 KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n"); 6 return; 7 } 8 fcntl(uevent_fd, F_SETFL, O_NONBLOCK); //設置文件狀態標誌為非阻塞模 9 //將uevent事件註冊到uevent_fd文件節點用以監聽uevent事件 10 if (healthd_register_event(uevent_fd, uevent_event)) 11 KLOG_ERROR(LOG_TAG, 12 "register for uevent events failed\n"); 13 }
我們可以看到android利用epoll監聽了三個文件節點的改變事件,分別是:通過gBinderfd監聽線程Binder通信事件;通過wakealarm_fd監聽wakealarm事件;
通過uevent_fd監聽wakealarm事件。至於如何監聽後面做詳細分析
在healthd_init中最後創建BatteryMonitor的對象,並將其初始化。BatteryMonitor主要接受healthd傳來的數據,做電池狀態的計算並更新。
我們可以看到在BatterMonitor中的init函數中有以下語句:
1 /*POWER_SUPPLY_SYSFS_PATH定義為"/sys/class/power_supply", 2 在init函數中打開系統該文件夾,然後一一讀取該文件夾下的文件內容*/ 3 DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH); 4 struct dirent* entry; 5 。。。。。。。 6 //在while迴圈中判斷該文件夾下各個文件節點的內容,並將其初始化給相關的參數 7 while ((entry = readdir(dir))) { 8 const char* name = entry->d_name; 9 。。。。。。 10 }
至此,healthd_init函數就分析完了,其主要工作就是:創建了三個文件節點用來監聽相應的三種事件改變;創建BatteryMonitor對象,並通過讀取/sys/class/power_supply將其初始化。
Healthd_init走完之後,接著就是調用healthd_mainloop函數,該函數維持了一個死迴圈,代碼如下:
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; /*這裡介紹一下輪詢機制中重要函數epoll_waite(). epoll_wait運行的道理是:等侍註冊在epfd上的socket fd的事務的產生, 若是產生則將產生的sokct fd和事務類型放入到events數組中。 且timeout如果為-1則為阻塞式,timeowout為0則表示非阻塞式。 可以看到代碼中timeout為-1,故為阻塞式輪詢,當epollfd上有事件發生, 則會走到下麵的處理邏輯。 */ 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); //處理事件 } if (!nevents) periodic_chores(); healthd_mode_ops->heartbeat(); } return; }
Healthd_mainloop中維持了一個死迴圈,死迴圈中變數nevents 表示從epollfd中輪循中監聽得到的事件數目,這裡介紹一下輪詢機制中重要函數epoll_waite().
epoll_wait運行的道理是:等侍註冊在epfd上的socket fd的事務的產生,若是產生則將產生的sokct fd和事務類型放入到events數組中。且timeout如果為-1則為阻塞式,timeowout為0則表示非阻塞式。可以看到代碼中timeout為-1,故為阻塞式輪詢,當epollfd上有事件發生,則會走到下麵的處理邏輯。事件處理主要在for迴圈中:
在periodic_chores()中調用到healthd_battery_update()更新電池狀態。
1 void healthd_battery_update(void) { 2 // Fast wake interval when on charger (watch for overheat); 3 // slow wake interval when on battery (watch for drained battery). 4 int new_wake_interval = gBatteryMonitor->update() ? //更新,調用BatteryMonitor的update函數,根據不同充電狀態設置不同的定時器喚醒周期 5 healthd_config.periodic_chores_interval_fast : 6 healthd_config.periodic_chores_interval_slow; 7 if (new_wake_interval != wakealarm_wake_interval) 8 wakealarm_set_interval(new_wake_interval); //new_wake_interval表示新的wakealarm喚醒間隔 9 // During awake periods poll at fast rate. If wake alarm is set at fast 10 // rate then just use the alarm; if wake alarm is set at slow rate then 11 // poll at fast rate while awake and let alarm wake up at slow rate when 12 // asleep. 13 if (healthd_config.periodic_chores_interval_fast == -1) 14 awake_poll_interval = -1; 15 else 16 awake_poll_interval = 17 new_wake_interval == healthd_config.periodic_chores_interval_fast ? 18 -1 : healthd_config.periodic_chores_interval_fast * 1000; 19 }
可以看出該函數並不長,new_wake_interval表示新的wakealarm喚醒間隔,通過調用BatteryMonitor的update函數(後面詳細分析如何更新),其返回值為是否處於充電狀態,當處於充電狀態,則喚醒間隔為healthd_config.periodic_chores_interval_fast(短間隔),當不再充電狀態時喚醒間隔為healthd_config.periodic_chores_interval_slow(長間隔)
當新的間隔變數new_wake_interval與舊的變數wakealarm_wake_interval不一樣,則將新的喚醒間隔設置成wakealarm的喚醒間隔;
awake_poll_internal作為下一次epoll_waite的timeout參數,在這裡將其更新,在充電狀態下awake_poll_internal為-1,沒有充電的狀態下awake_poll_internal為60000ms
healthd主流程都是在main函數中處理,至此main已經分析完成,其簡要流程圖如下
Healthd處理邏輯
初始化處理
前面將healthd模塊中main函數分析完了,其主要工作流程有個大概的瞭解,但是其詳細處理邏輯並未做分析,在此之後,對Healthd的初始化,事件處理,狀態更新將做一個詳細的分析。
前面已經說過在healthd_init中創建了三個文件節點gBinderfd,uevent_fd,wakealarm_fd,並用以註冊監聽三種事件,註冊監聽都是通過healthd_register_event函數實現的。
healthd_register_event(gBinderFd, binder_event);
healthd_register_event(wakealarm_fd, wakealarm_event);
healthd_register_event(uevent_fd, uevent_event);
其healthd_register_event實現代碼如下:
1 int healthd_register_event(int fd, void (*handler)(uint32_t)) { 2 struct epoll_event ev; 3 ev.events = EPOLLIN | EPOLLWAKEUP; 4 ev.data.ptr = (void *)handler; 5 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { 6 KLOG_ERROR(LOG_TAG, 7 "epoll_ctl failed; errno=%d\n", errno); 8 return -1; 9 } 10 eventct++; 11 return 0; 12 }
函數將相應的文件節點事件賦值為函數的第二個形參,也就是說相應的gBinderfd的事件處理函數為binder_event函數,同理wakealarm_fd,ueven_fd的事件事件處理分別為wakealarm_event,uevent_event函數。然後將其三個文件節點加入到epollfd中。
事件獲取與處理
Healthd中維持了一個阻塞式的死迴圈healthd_mainloop,在該函數中提供阻塞式的監聽已發送的事件函數epoll_wait(),healthd_mainloop中有如下代碼
1 nevents = epoll_wait(epollfd, events, eventct, timeout); 2 if (nevents == -1) { 3 if (errno == EINTR) 4 continue; 5 KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n"); 6 break; 7 }
當epoll_waite接受到gBinderfd,wakealarm_fd,uevent_fd其中的事件,便會將監聽到的事件加入到event數組中。在for迴圈中做處理,for迴圈中代碼看起來非常難懂,其實if判斷的便是event有沒有相應的處理函數,在前面註冊事件時候已經提到,三種句柄上的事件都有對應的處理函數,也就是當收到gBinderfd上的事件,便用binder_event函數處理,當收到uevent_fd上的事件便用uevent_event處理,當收到wakealarm_fd上的事件便用wakealarm_event處理。
這裡以較為重要的uevent_event事件處理為例:
1 #define UEVENT_MSG_LEN 2048 2 static void uevent_event(uint32_t /*epevents*/) { 3 char msg[UEVENT_MSG_LEN+2]; 4 char *cp; 5 int n; 6 n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN); 7 if (n <= 0) 8 return; 9 if (n >= UEVENT_MSG_LEN) /* overflow -- discard */ 10 return; 11 msg[n] = '\0'; 12 msg[n+1] = '\0'; 13 cp = msg; 14 while (*cp) { 15 if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) { 16 healthd_battery_update(); 17 break; 18 } 19 /* advance to after the next \0 */ 20 while (*cp++) 21 ; 22 } 23 }
處理函數首先從uevent_fd 獲取事件數目,然後迴圈判斷是否是來自與power_supply目錄下的事件,如果是,則調用到healthd_battery_update中去更新電池狀態。
更新電池狀態
當收到事件,做一些判斷工作便需要更新電池狀態,其更新函數為healthd.cpp下的healthd_battery_update函數,但是主要更新並不在heathd中完成的,而是在BatteryMonitor中的update函數:
1 bool BatteryMonitor::update(void) { 2 bool logthis; 3 //清除原有屬性值 4 props.chargerAcOnline = false; 5 props.chargerUsbOnline = false; 6 props.chargerWirelessOnline = false; 7 props.batteryStatus = BATTERY_STATUS_UNKNOWN; 8 props.batteryHealth = BATTERY_HEALTH_UNKNOWN; 9 props.maxChargingCurrent = 0; 10 if (!mHealthdConfig->batteryPresentPath.isEmpty()) 11 props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath); 12 else 13 props.batteryPresent = mBatteryDevicePresent; 14 props.batteryLevel = mBatteryFixedCapacity ? 15 mBatteryFixedCapacity : 16 getIntField(mHealthdConfig->batteryCapacityPath); 17 props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000; 18 props.batteryTemperature = mBatteryFixedTemperature ? 19 mBatteryFixedTemperature : 20 getIntField(mHealthdConfig->batteryTemperaturePath); 21 // For devices which do not have battery and are always plugged 22 // into power souce. 23 if (mAlwaysPluggedDevice) { 24 props.chargerAcOnline = true; 25 props.batteryPresent = true; 26 props.batteryStatus = BATTERY_STATUS_CHARGING; 27 props.batteryHealth = BATTERY_HEALTH_GOOD; 28 } 29 30 const int SIZE = 128; 31 char buf[SIZE]; 32 String8 btech; 33 //讀取/sys/class/power_supply下文件節點信息 34 if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0) 35 props.batteryStatus = getBatteryStatus(buf); 36 if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0) 37 props.batteryHealth = getBatteryHealth(buf); 38 if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0) 39 props.batteryTechnology = String8(buf); 40 41 /*在init函數中將healthd_config 對象傳入,並且將裡面的成員的一些地址信息去初始化保存起來。 42 主要是保存一些地址信息,以及充電方式。在BatteryMonitor初始化中,heathd_config傳入init函數中, 43 賦值為mHealthdConfig,上面一段主要是讀取/sys/class/power_supply下的 44 文件節點信息初更新電池數據屬性值 45 */ 46 47 unsigned int i; 48 //遍歷/sys/class/power_supply下的文件節點獲取信息 49 for (i = 0; i < mChargerNames.size(); i++) { 50 String8 path; 51 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, 52 mChargerNames[i].string()); 53 //讀取子目錄文件online的值,online值為1使用,0值沒使用 54 if (readFromFile(path, buf, SIZE) > 0) { 55 if (buf[0] != '0') { 56 path.clear(); 57 path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, 58 mChargerNames[i].string()); 59 switch(readPowerSupplyType(path)) { //根據讀取的值將相應的屬性值置true 60 case ANDROID_POWER_SUPPLY_TYPE_AC: 61 props.chargerAcOnline = true; //true,false在framework層用於充電狀態判讀 62 break; 63 case ANDROID_POWER_SUPPLY_TYPE_USB: 64 props.chargerUsbOnline = true; 65 break; 66 case ANDROID_POWER_SUPPLY_TYPE_WIRELESS: 67 props.chargerWirelessOnline = true; 68 break; 69 default: 70 KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n", 71 mChargerNames[i].string()); 72 } 73 path.clear(); 74 path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH, 75 mChargerNames[i].string()); 76 if (access(path.string(), R_OK) == 0) { 77 int maxChargingCurrent = getIntField(path); 78 if (props.maxChargingCurrent < maxChargingCurrent) { 79 props.maxChargingCurrent = maxChargingCurrent; 80 } 81 } 82 } 83 } 84 } 85 logthis = !healthd_board_battery_update(&props); //此處必返回0 86 if (logthis) { //此處必執行 87 char dmesgline[256]; 88 //更新props的各個屬性值,props將會被傳到framework層進行數據傳輸 89 if (props.batteryPresent) { 90 snprintf(dmesgline, sizeof(dmesgline), 91 "battery l=%d v=%d t=%s%d.%d h=%d st=%d", 92 props.batteryLevel, props.batteryVoltage, 93 props.batteryTemperature < 0 ? "-" : "", 94 abs(props.batteryTemperature / 10), 95 abs(props.batteryTemperature % 10), props.batteryHealth, 96 props.batteryStatus); 97 if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { 98 int c = getIntField(mHealthdConfig->batteryCurrentNowPath); 99 char b[20]; 100 snprintf(b, sizeof(b), " c=%d", c / 1000); 101 strlcat(dmesgline, b, sizeof(dmesgline)); 102 } 103 } else { 104 snprintf(dmesgline, sizeof(dmesgline), 105 "battery none"); 106 } 107 size_t len = strlen(dmesgline); 108 snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s", 109 props.chargerAcOnline ? "a" : "", 110 props.chargerUsbOnline ? "u" : "", 111 props.chargerWirelessOnline ? "w" : ""); 112 log_time realtime(CLOCK_REALTIME); 113 time_t t = realtime.tv_sec; 114 struct tm *tmp = gmtime(&t); 115 if (tmp) { 116 static const char fmt[] = " %Y-%m-%d %H:%M:%S.XXXXXXXXX UTC"; 117 //前的電量級別,電壓,溫度,健康狀況,電池狀態以及充放電倍率存入dmesgline變數中, 118 len = strlen(dmesgline); 119 if ((len < (sizeof(dmesgline) - sizeof(fmt) - 8)) // margin 120 && strftime(dmesgline + len, sizeof(dmesgline) - len, 121 fmt, tmp)) { 122 char *usec = strchr(dmesgline + len, 'X'); 123 if (usec) { 124 len = usec - dmesgline; 125 snprintf(dmesgline + len, sizeof(dmesgline) - len, 126 "%09u", realtime.tv_nsec); 127 usec[9] = ' '; 128 } 129 } 130 } 131 KLOG_WARNING(LOG_TAG, "%s\n", dmesgline); //向log記錄電池當前各種狀態信息 132 } 133 healthd_mode_ops->battery_update(&props); //更新電池 134 135 /*回是否在充電狀態個update函數做完更新數據,記錄數據到log之後, 136 然後調用到BatteryPropertiesRegistrar的update函數繼續更新電池狀態, 137 最後返回值為是否處於充電狀態。*/ 138 return props.chargerAcOnline | props.chargerUsbOnline | 139 props.chargerWirelessOnline; 140 }
BatteryPropertiesRegistrar的update函數未作任何操作調用Healthd_mode_android.cpp中的healthd_mode_android_battery_update函數,我們可以看看該函數
1 void healthd_mode_android_battery_update( 2 struct android::BatteryProperties *props) { //props攜帶各種需要傳輸的數據 3 if (gBatteryPropertiesRegistrar != NULL) 4 //調用BatteryPropertiesRegistrar的notifyListeners去通知props改變了 5 gBatteryPropertiesRegistrar->notifyListeners(*props); 6 return; 7 }
這裡這裡直接調用到BatteryPropertiesRegistrar的notifyListeners去通知props改變了,props是什麼呢?props是定義的一個BatteryProperties屬性集,裡面的成員變數包含了所有的電池狀態信息,在update開始便通過讀取各個文件節點的實時數據更新電池屬性props,更新完成後通過BatteryPropertiesRegistrar通知其屬性監聽者去更新狀態,但是誰是監聽呢?
我們可以看到framework層中的BatteryService.java的onStart函數中有如下代碼:
1 public void onStart() { 2 IBinder b = ServiceManager.getService("batteryproperties"); 3 final IBatteryPropertiesRegistrar batteryPropertiesRegistrar = 4 IBatteryPropertiesRegistrar.Stub.asInterface(b); 5 try { 6 batteryPropertiesRegistrar.registerListener(new BatteryListener()); //監聽事件 7 } catch (RemoteException e) { 8 // Should never happen. 9 } 10 publishBinderService("battery", new BinderServic