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.h和system/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系统对当前电源类型的判别逻辑是这样的:
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 |
|