Healthd是Android4.4之后提出来的一种中介模型,该模型向下监听来自底层的电池事件,向上传递电池数据信息给Framework层的BatteryService用以计算电池电量相关状态信息,BatteryServcie通过传递来的数据来计算电池电量显示,剩余电量,电量级别等信息,如果收到过温报警或者严重低电报警等信息,系统会直接关机,保护硬件。
Healthd模块代码是在system/core/healthd/,其模块入口在healthd的main函数,函数代码如下:
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函数并不长,但是其作用确实巨大的,main函数起着一个统筹兼顾的作用,其他各个模块函数去做一些具体相应的工作,最后汇总到main函数中被调用。
代码中开始便是解析参数,healthd_mode_ops是一个关于充电状态结构体变量,结构体变量里的参数是函数指针,在初始化时指向各个不同的操作函数,当开机充电时变量赋值为&android_ops,关机充电时候变量赋值为&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; }
创建一个epoll的变量将其赋值给epollfd,在healthd_board_init中未作任何事便返回了。
healthd_mode_ops->init调用有两种情况:关机情况下调用charger_ops的init函数;开机情况下调用android_ops的init函数,这里就开机情况来分析。android_ops的init函数指针指向healthd_mode_android_init函数
代码如下:
void healthd_mode_android_init(struct healthd_config* /*config*/) { ProcessState::self()->setThreadPoolMaxThreadCount(0);//线程池里最大线程数 IPCThreadState::self()->disableBackgroundScheduling(true);//禁用后台调度 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函数:
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); }
首先创建一个wakealarm_fd的定时器与之对应的文件描述符,healthd_register_event将wakealarm事件注册到wakealarm_fd文件节点用以监听wakealarm事件,wakealarm_set_interval设置alarm唤醒的间隔
再看看uevent_init函数:
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"); }
创建并打开一个64k的socket文件描述符uevent_fd,设置文件状态标志为非阻塞模,将uevent事件注册到uevent_fd文件节点用以监听uevent事件。
我们可以看到android利用epoll监听了三个文件节点的改变事件,分别是:通过gBinderfd监听线程Binder通信事件;通过wakealarm_fd监听wakealarm事件;通过uevent_fd监听wakealarm事件。至于如何监听后面做详细分析
在healthd_init中最后创建BatteryMonitor的对象,并将其初始化。BatteryMonitor主要接受healthd传来的数据,做电池状态的计算并更新。
我们可以看到在BatterMonitor中的init函数中有以下语句:
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函数中打开系统该文件夹,然后一一读取该文件夹下的文件内容,在while循环中判断该文件夹下各个文件节点的内容,并将其初始化给相关的参数.
至此,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; 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中维持了一个死循环,死循环中变量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()更新电池状态。
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; }
可以看出该函数并不长,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模块中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实现代码如下:
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; }
函数将相应的文件节点事件赋值为函数的第二个形参,也就是说相应的gBinderfd的事件处理函数为binder_event函数,同理wakealarm_fd,ueven_fd的事件事件处理分别为wakealarm_event,uevent_event函数。然后将其三个文件节点加入到epollfd中。
Healthd中维持了一个阻塞式的死循环healthd_mainloop,在该函数中提供阻塞式的监听已发送的事件函数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其中的事件,便会将监听到的事件加入到event数组中。在for循环中做处理,for循环中代码看起来非常难懂,其实if判断的便是event有没有相应的处理函数,在前面注册事件时候已经提到,三种句柄上的事件都有对应的处理函数,也就是当收到gBinderfd上的事件,便用binder_event函数处理,当收到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] = '