Android电源管理-Healthd

OS:Android 4.4.2

Android电源管理底层用的是Linux power supply框架。驱动部分不叙述。只看JAVA、JNI和CPP应用层。

从Android 4.4开始,Google专门提供了一个healthd来监控电源状态。它的路径在:system/core/healthd,编译出来的文件为/sbin/healthd

看一下healthd.cpp中的main函数:

1 int main(int argc, char **argv) {
2     int ch;
3  
4     klog_set_level(KLOG_LEVEL);
5  
6     while ((ch = getopt(argc, argv, "n")) != -1) {
7         switch (ch) {
8         case 'n':
9             nosvcmgr = true;
10             break;
11         case '?':
12         default:
13             KLOG_WARNING(LOG_TAG, "Unrecognized healthd option: %c\n", ch);
14         }
15     }
16  
17     healthd_board_init(&healthd_config);
18     wakealarm_init();
19     uevent_init();
20     binder_init();
21     gBatteryMonitor = new BatteryMonitor();
22     gBatteryMonitor->init(&healthd_config, nosvcmgr);
23  
24     healthd_mainloop();
25     return 0;
26 }

这里引人关注的是最后调用的healthd_mainloop(),仅凭函数名就能知道会进入一个无限循环,这样也就能达到监控电源状态的目的了。跟踪代码看一下这个函数的定义:

1 static void healthd_mainloop(void) {
2     struct epoll_event ev;
3     int epollfd;
4     int maxevents = 0;
5  
6     epollfd = epoll_create(MAX_EPOLL_EVENTS);
7     if (epollfd == -1) {
8         KLOG_ERROR(LOG_TAG,
9                    "healthd_mainloop: epoll_create failed; errno=%d\n",
10                    errno);
11         return;
12     }
13  
14     if (uevent_fd >= 0) {
15         ev.events = EPOLLIN | EPOLLWAKEUP;
16         ev.data.ptr = (void *)uevent_event;
17         if (epoll_ctl(epollfd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1)
18             KLOG_ERROR(LOG_TAG,
19                        "healthd_mainloop: epoll_ctl for uevent_fd failed; errno=%d\n",
20                        errno);
21         else
22             maxevents++;
23     }
24  
25     if (wakealarm_fd >= 0) {
26         ev.events = EPOLLIN | EPOLLWAKEUP;
27         ev.data.ptr = (void *)wakealarm_event;
28         if (epoll_ctl(epollfd, EPOLL_CTL_ADD, wakealarm_fd, &ev) == -1)
29             KLOG_ERROR(LOG_TAG,
30                        "healthd_mainloop: epoll_ctl for wakealarm_fd failed; errno=%d\n",
31                        errno);
32         else
33             maxevents++;
34    }
35  
36     if (binder_fd >= 0) {
37         ev.events = EPOLLIN | EPOLLWAKEUP;
38         ev.data.ptr= (void *)binder_event;
39         if (epoll_ctl(epollfd, EPOLL_CTL_ADD, binder_fd, &ev) == -1)
40             KLOG_ERROR(LOG_TAG,
41                        "healthd_mainloop: epoll_ctl for binder_fd failed; errno=%d\n",
42                        errno);
43         else
44             maxevents++;
45    }
46  
47     while (1) {
48         struct epoll_event events[maxevents];
49         int nevents;
50  
51         IPCThreadState::self()->flushCommands();
52         nevents = epoll_wait(epollfd, events, maxevents, awake_poll_interval);
53  
54         if (nevents == -1) {
55             if (errno == EINTR)
56                 continue;
57             KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
58             break;
59         }
60  
61         for (int n = 0; n < nevents; ++n) {
62             if (events[n].data.ptr)
63                 (*(void (*)())events[n].data.ptr)();
64         }
65  
66         if (!nevents)
67             periodic_chores();
68     }
69  
70     return;
71 }

果不其然,我们看到了while(1)。Android使用了linux epoll来侦听三个文件描述符的变化,分别是uevent_fd、wakealarm_fd和binder_fd。以uevent_fd为例,在这个描述符上关心的事件有EPOLLIN和EPOLLWAKEUP,当底层出现这两个事件的时候,会回调函数uevent_event(),看看这个函数会做些什么:

1 #define UEVENT_MSG_LEN 1024
2 static void uevent_event(void) {
3     char msg[UEVENT_MSG_LEN+2];
4     char *cp;
5     int n;
6  
7     n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
8     if (n <= 0)
9         return;
10     if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
11         return;
12  
13     msg[n] = '\0';
14     msg[n+1] = '\0';
15     cp = msg;
16  
17     while (*cp) {
18         if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
19             battery_update();
20             break;
21         }
22  
23         /* advance to after the next \0 */
24         while (*cp++)
25             ;
26     }
27 }

它会读取socket中的字符串,然后判断事件来源是否是由kernel的power_supply发出的,如果是,那就调用battery_update()更新电源状态。下面来看看battery_update()是如何更新电源状态的:

1 static void 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  
5    int new_wake_interval = gBatteryMonitor->update() ?
6        healthd_config.periodic_chores_interval_fast :
7            healthd_config.periodic_chores_interval_slow;
8  
9     if (new_wake_interval != wakealarm_wake_interval)
10             wakealarm_set_interval(new_wake_interval);
11  
12     // During awake periods poll at fast rate.  If wake alarm is set at fast
13     // rate then just use the alarm; if wake alarm is set at slow rate then
14     // poll at fast rate while awake and let alarm wake up at slow rate when
15     // asleep.
16  
17     if (healthd_config.periodic_chores_interval_fast == -1)
18         awake_poll_interval = -1;
19     else
20         awake_poll_interval =
21             new_wake_interval == healthd_config.periodic_chores_interval_fast ?
22                 -1 : healthd_config.periodic_chores_interval_fast * 1000;
23 }

这里的重点是gBatteryMonitor->update(),gBatteryMonitor的类型为BatteryMonitor。那接下来就把目光专注到system/core/healthd/BatteryMonitor.hsystem/core/healthd/BatteryMonitor.cpp

在BatteryMonitor.h中,我们看到了一个enum,它列出了Android所支持的电源类型:

查看源代码
打印 帮助
1 enum PowerSupplyType {
2     ANDROID_POWER_SUPPLY_TYPE_UNKNOWN = 0,
3     ANDROID_POWER_SUPPLY_TYPE_AC,
4     ANDROID_POWER_SUPPLY_TYPE_USB,
5     ANDROID_POWER_SUPPLY_TYPE_WIRELESS,
6     ANDROID_POWER_SUPPLY_TYPE_BATTERY
7 };

可以看出Android当前支持的电源类型有4种:AC,USB,WIRELESS,BATTERY。在这里你可以增加你自己的电源类型,比如CAR(车载)… …

把目光聚焦到BatteryMonitor.cpp,这里有获取电源状态的核心代码。但是在继续看代码之前,我们先来看一下power_supply电源驱动是如何管理系统中的不同电源类型的。

adb shell进入到/sys/class/power_supply目录,我们可以看到power_supply驱动创建的一些运行时文件(我的设备是Nuxus 7, Android 4.4.2, kernel 3.4.0):

1 adb root
2 adb shell
3 cd /sys/class/power_supply
4 ll

输出如下:

1 lrwxrwxrwx root  root  2014-09-19 14:30 ac -> ../../devices/i2c-0/0-0055/power_supply/ac
2 lrwxrwxrwx root  root  2014-09-19 14:30 battery -> ../../devices/i2c-0/0-0055/power_supply/battery
3 lrwxrwxrwx root  root  2014-09-19 14:30 usb -> ../../devices/i2c-0/0-0055/power_supply/usb
4 lrwxrwxrwx root  root  2014-09-19 14:30 wireless -> ../../devices/i2c-0/0-0055/power_supply/wireless

看文件名称就能知道其含义,但是问题是这里一下子列出了4种电源类型,Android系统究竟是怎么判断当前使用的是那一种呢?要回答这个问题,我们不妨进入其中任一个文件夹,看看里面记录的是些什么。

1 cd usb
2 ls

输出如下:

1 lrwxrwxrwx root root        2014-09-20 22:03 device -> ../../../0-0055
2 -r--r--r-- root root   4096 2014-09-19 14:30 online
3 drwxr-xr-x root root        2014-09-20 22:03 power
4 lrwxrwxrwx root root        2014-09-20 22:03 subsystem -> ../../../../../class/power_supply
5 -r--r--r-- root root   4096 2014-09-19 18:24 type
6 -rw-r--r-- root root    4096 2014-09-20 22:03 uevent

查看online文件,发现里面的值为1,这是因为我的设备正在使用USB电源,此时如果查看ac或者wireless文件夹中的online文件,你会发现其值为0;
再查看type文件,发现里面的值为USB,这个文件记录了对于的名称。
所以Android系统对当前电源类型的判别逻辑是这样的:

  • 遍历所有系统支持的电源方式
  • 查看online的值,值为1即是当前电源方式
      回到代码,我们看看是不是真的这样做的呢。
      查看 system/core/healthd/BatteryMonitor.cpp,关注下面的片段:

      1 for (i = 0; i < mChargerNames.size(); i++) {
      2     String8 path;
      3     path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
      4                       mChargerNames[i].string());
      5  
      6     if (readFromFile(path, buf, SIZE) > 0) {
      7         if (buf[0] != '0') {
      8             path.clear();
      9             path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
      10                               mChargerNames[i].string());
      11             switch(readPowerSupplyType(path)) {
      12             case ANDROID_POWER_SUPPLY_TYPE_AC:
      13                 

你可能感兴趣的:(charger&battery)