android6.0系统Healthd分析及低电量自动关机流程
Posted __L_jin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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=%dAndroid6.0系统启动流程分析一:init进程