1. 程式人生 > >android6.0系統Healthd分析及低電量自動關機流程

android6.0系統Healthd分析及低電量自動關機流程

 

系統平臺: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 BinderService());
11      publishLocalService(BatteryManagerInternal.class, new LocalService());
12  }


我們在初始化的時候已經提到過,當healthd初始化時候會建立BatteryPropertiesRegistrar的物件並將其publish註冊到系統服務中,註冊服務的語句如下:
defaultServiceManager()->addService(String16(“batteryproperties”), this);

所以BatteryService在這裡獲取該服務,並以此註冊其監聽器為BatteryListener(),該監聽器監聽到BatteryProperties改變便會呼叫到BatteryService的update函式,去做電池電量相關計算以及顯示。

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


============================================================================================================
下面以低電量關機為例分析BatteryService監聽到資料後的處理流程
電池監聽函式如下:

 1  private final class BatteryListener extends IBatteryPropertiesListener.Stub {
 2      @Override
 3      public void batteryPropertiesChanged(BatteryProperties props) {
 4          final long identity = Binder.clearCallingIdentity();
 5          try {
 6              BatteryService.this.update(props);   //監聽到資料呼叫BatteryService的update函式
 7          } finally {
 8              Binder.restoreCallingIdentity(identity);
 9          }
10     }
11  }


函式呼叫流程:
BatteryListener
==>BatteryService.this.update(props)
   ==>processValuesLocked(false);
     ==>shutdownIfNoPowerLocked(); //在此函式中對充電資料進行處理,根據不同的充電條件判斷是否關機

shutdownIfNoPowerLocked函式程式碼如下:

 1  private void shutdownIfNoPowerLocked() {
 2      // shut down gracefully if our battery is critically low and we are not powered.
 3      // wait until the system has booted before attempting to display the shutdown dialog.
 4      //isPoweredLocked判斷充電的狀態,如果電量為零但又沒有接充電器的情況下關機
 5      if (mBatteryProps.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
 6          mHandler.post(new Runnable() {
 7              @Override
 8              public void run() {   //執行關機的程式碼
 9                  if (ActivityManagerNative.isSystemReady()) {
10                      Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
11                      intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
12                      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
13                      mContext.startActivityAsUser(intent, UserHandle.CURRENT);
14                  }
15              }
16          });
17      }
18  }


isPoweredLocked函式程式碼如下:

 1  private boolean isPoweredLocked(int plugTypeSet) {
 2      // assume we are powered if battery state is unknown so
 3      // the "stay on while plugged in" option will work.
 4      
 5      //如果狀態不確定,預設為在充電
 6      if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {   
 7          return true;
 8      }
 9      //為插入AC並且為AC充電狀態則返回true不關機
10      if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mBatteryProps.chargerAcOnline) {
11          return true;
12      }
13      //插入USB並且為USB充電狀態則返回true不關機
14      if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mBatteryProps.chargerUsbOnline) {
15          return true;
16      }
17      //無線充電
18      if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mBatteryProps.chargerWirelessOnline) {
19          return true;
20      }
21      return false;
22  }


總結:
Healthd是framework層傳遞來自底層電池事件資訊並呼叫相關模組更新電池狀態的一箇中間層,其向下監聽來自底層PMU驅動上報的uevent電池事件,向上呼叫BatteryService去計算電池,電量,使用等相關資訊,它通過一個阻塞式的死迴圈不斷監聽底層三個檔案節點上的事件資訊,當監聽到事件便呼叫到BatteryMonitor執行更新操作,通過BatteryService.java中註冊監聽電池屬性改變的函式,當電池屬性資訊發生改變,即回撥到BatteryService中做更新操作,更新完成一次電池事件的上報到更新整個流程就完成;總之Healthd是連線Battery模組framework中java層與HAL層互動的主要通道。

附:在此部落格上整理新增而來
  https://blog.csdn.net/u011311586/article/details/51082685