首页 >> 大全

android6.0系统Healthd详解

2023-11-29 大全 31 作者:考证青年

概述

是.4之后提出来的一种中介模型,该模型向下监听来自底层的电池事件,向上传递电池数据信息给层的用以计算电池电量相关状态信息,通过传递来的数据来计算电池电量显示,剩余电量,电量级别等信息,如果收到过温报警或者严重低电报警等信息,系统会直接关机,保护硬件。

主模块处理流程

模块代码是在/core//,其模块入口在的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: %c\n",optopt);exit(1);}}}ret = healthd_init();if (ret) {KLOG_ERROR("Initialization failed, exiting\n");exit(2);}healthd_mainloop();KLOG_ERROR("Main loop terminated, exiting\n");return 3;}

可以看出Main函数并不长,但是其作用确实巨大的,main函数起着一个统筹兼顾的作用,其他各个模块函数去做一些具体相应的工作,最后汇总到main函数中被调用。

代码中开始便是解析参数,是一个关于充电状态结构体变量,结构体变量里的参数是函数指针,在初始化时指向各个不同的操作函数,当开机充电时变量赋值为&,关机充电时候变量赋值为&。

在ret=();中进行一些初始化工作。

static int healthd_init() {epollfd = epoll_create(MAX_EPOLL_EVENTS);if (epollfd == -1) {KLOG_ERROR(LOG_TAG,"epoll_create failed; errno=%d\n",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的变量将其赋值给,在中未作任何事便返回了。

->init调用有两种情况:关机情况下调用的init函数;开机情况下调用的init函数,这里就开机情况来分析。的init函数指针指向_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 failed\n");}gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();gBatteryPropertiesRegistrar->publish();
}

前面三条语句做初始化工作,设置线程池最大线程数,禁用后台调度,以及将加入到epoll中。nt将事件注册到文件节点用以监听事件。->将""这个注册到中

再来看看函数:

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 failed\n");return;}if (healthd_register_event(wakealarm_fd, wakealarm_event))KLOG_ERROR(LOG_TAG,"Registration of wakealarm event failed\n");wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
}

首先创建一个的定时器与之对应的文件描述符,nt将事件注册到文件节点用以监听事件,al设置alarm唤醒的间隔

再看看函数:

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 failed\n");return;}fcntl(uevent_fd, F_SETFL, O_NONBLOCK);if (healthd_register_event(uevent_fd, uevent_event))KLOG_ERROR(LOG_TAG,"register for uevent events failed\n");
}

创建并打开一个64k的文件描述符,设置文件状态标志为非阻塞模,将事件注册到文件节点用以监听事件。

我们可以看到利用epoll监听了三个文件节点的改变事件,分别是:通过监听线程通信事件;通过监听事件;通过监听事件。至于如何监听后面做详细分析

在中最后创建的对象,并将其初始化。主要接受传来的数据,做电池状态的计算并更新。

我们可以看到在中的init函数中有以下语句:

DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);struct dirent* entry;。。。。。。。while ((entry = readdir(dir))) {const char* name = entry->d_name;。。。。。。}

android60__android6.0手机

ATH定义为"/sys/class/",在init函数中打开系统该文件夹,然后一一读取该文件夹下的文件内容,在while循环中判断该文件夹下各个文件节点的内容,并将其初始化给相关的参数.

至此,函数就分析完了,其主要工作就是:创建了三个文件节点用来监听相应的三种事件改变;创建对象,并通过读取/sys/class/将其初始化。

走完之后,接着就是调用函数,该函数维持了一个死循环,代码如下:

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 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;}

中维持了一个死循环,死循环中变量表示从中轮循中监听得到的事件数目,这里介绍一下轮询机制中重要函数().

运行的道理是:等侍注册在epfd上的的事务的产生,若是产生则将产生的和事务类型放入到数组中。且如果为-1则为阻塞式,为0则表示非阻塞式。可以看到代码中为-1,故为阻塞式轮询,当上有事件发生,则会走到下面的处理逻辑。事件处理主要在for循环中:

在()中调用到te()更新电池状态。

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;Elseawake_poll_interval = new_wake_interval == healthd_config.periodic_chores_interval_fast ?-1 : healthd_config.periodic_chores_interval_fast * 1000;}

可以看出该函数并不长,表示新的唤醒间隔,通过调用的函数(后面详细分析如何更新),其返回值为是否处于充电状态,当处于充电状态,则唤醒间隔为.(短间隔),当不再充电状态时唤醒间隔为.(长间隔)

当新的间隔变量与旧的变量val不一样,则将新的唤醒间隔设置成的唤醒间隔;

作为下一次的参数,在这里将其更新,在充电状态下为-1,没有充电的状态下为

主流程都是在main函数中处理,至此main已经分析完成,其简要流程图如下

处理逻辑 初始化处理

前面将模块中main函数分析完了,其主要工作流程有个大概的了解,但是其详细处理逻辑并未做分析,在此之后,对的初始化,事件处理,状态更新将做一个详细的分析。

前面已经说过在中创建了三个文件节点,,,并用以注册监听三种事件,注册监听都是通过nt函数实现的。

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=%d\n", errno);return -1;}eventct++;return 0;}

函数将相应的文件节点事件赋值为函数的第二个形参,也就是说相应的的事件处理函数为函数,同理,的事件事件处理分别为,函数。然后将其三个文件节点加入到中。

事件获取与处理

中维持了一个阻塞式的死循环,在该函数中提供阻塞式的监听已发送的事件函数(),中有如下代码

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);}

当接受到,,其中的事件,便会将监听到的事件加入到event数组中。在for循环中做处理,for循环中代码看起来非常难懂,其实if判断的便是event有没有相应的处理函数,在前面注册事件时候已经提到,三种句柄上的事件都有对应的处理函数,也就是当收到上的事件,便用函数处理,当收到上的事件便用处理,当收到上的事件便用处理。

这里以较为重要的事件处理为例:

#define UEVENT_MSG_LEN 2048static 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] = '\0';msg[n+1] = '\0';cp = msg;while (*cp) {if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {healthd_battery_update();break;}/* advance to after the next \0 */while (*cp++);}}

_android60_android6.0手机

处理函数首先从获取事件数目,然后循环判断是否是来自与目录下的事件,如果是,则调用到te中去更新电池状态。

更新电池状态

当收到事件,做一些判断工作便需要更新电池状态,其更新函数为.cpp下的te函数,但是主要更新并不在中完成的,而是在中的函数,其代码较多,分段分析:

props.chargerAcOnline = false;props.chargerUsbOnline = false;props.chargerWirelessOnline = false;props.batteryStatus = BATTERY_STATUS_UNKNOWN;props.batteryHealth = BATTERY_HEALTH_UNKNOWN;if (!mHealthdConfig->batteryPresentPath.isEmpty())props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);elseprops.batteryPresent = mBatteryDevicePresent;props.batteryLevel = mBatteryFixedCapacity ?mBatteryFixedCapacity :getIntField(mHealthdConfig->batteryCapacityPath);props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;props.batteryTemperature = mBatteryFixedTemperature ?mBatteryFixedTemperature :getIntField(mHealthdConfig->batteryTemperaturePath);const int SIZE = 128;char buf[SIZE];String8 btech;if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0)props.batteryStatus = getBatteryStatus(buf);if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0)props.batteryHealth = getBatteryHealth(buf);if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0)props.batteryTechnology = String8(buf);

在init函数中将对象传入,并且将里面的成员的一些地址信息去初始化保存起来。主要是保存一些地址信息,以及充电方式。在初始化中,传入init函数中,赋值为,上面一段主要是读取/sys/class/下的文件节点信息初更新电池数据属性值,

path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,mChargerNames[i].string());if (readFromFile(path, buf, SIZE) > 0) {if (buf[0] != '0') {path.clear();path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,mChargerNames[i].string());switch(readPowerSupplyType(path)) {case ANDROID_POWER_SUPPLY_TYPE_AC:props.chargerAcOnline = true;break;case ANDROID_POWER_SUPPLY_TYPE_USB:props.chargerUsbOnline = true;break;case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:props.chargerWirelessOnline = true;break;default:KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",mChargerNames[i].string());}}}

这一段代码其实实现的功能很简单,通过读取/sys/class//文件夹下不同类型的充电设备的节点,如果不为空则表示处于充电状态,判断充电类型为AC充电器,usb充电还是无线充电并将其相应的属性置true。

char dmesgline[256];if (props.batteryPresent) {snprintf(dmesgline, sizeof(dmesgline),"battery l=%d v=%d t=%s%d.%d h=%d st=%d",props.batteryLevel, props.batteryVoltage,props.batteryTemperature < 0 ? "-" : "",abs(props.batteryTemperature / 10),abs(props.batteryTemperature % 10), props.batteryHealth,props.batteryStatus);if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {int c = getIntField(mHealthdConfig->batteryCurrentNowPath);char b[20];snprintf(b, sizeof(b), " c=%d", c / 1000);strlcat(dmesgline, b, sizeof(dmesgline));}

将电池当前的电量级别,电压,温度,健康状况,电池状态以及充放电倍率存入变量中,在后面会将电池充电类型,电池使用时间都以字符串存入变量中,然后:

(,"%s\n",);//向log记录电池当前各种状态信息

->(&props);//更新电池

.|props.|

props.e;//返回是否在充电状态

整个函数做完更新数据,记录数据到log之后,然后调用到的函数继续更新电池状态,最后返回值为是否处于充电状态。

的函数未作任何操作调用.cpp中的函数,我们可以看看该函数

void healthd_mode_android_battery_update(struct android::BatteryProperties *props) {if (gBatteryPropertiesRegistrar != NULL)gBatteryPropertiesRegistrar->notifyListeners(*props);return;}

这里这里直接调用到的去通知props改变了,props是什么呢?props是定义的一个属性集,里面的成员变量包含了所有的电池状态信息,在开始便通过读取各个文件节点的实时数据更新电池属性props,更新完成后通过通知其属性监听者去更新状态,但是谁是监听呢?

我们可以看到层中的.java的函数中有如下代码:

public void onStart() {IBinder b = ServiceManager.getService("batteryproperties");final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =IBatteryPropertiesRegistrar.Stub.asInterface(b);try {batteryPropertiesRegistrar.registerListener(new BatteryListener());} catch (RemoteException e) {// Should never happen.}

我们在初始化的时候已经提到过,当初始化时候会创建的对象并将其注册到系统服务中,注册服务的语句如下:

r()->((""),this);

所以在这里获取该服务,并以此注册其监听器为(),该监听器监听到改变便会调用到的函数,去做电池电量相关计算以及显示。

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

总结 是层传递来自底层电池事件信息并调用相关模块更新电池状态的一个中间层,其向下监听来自底层PMU驱动上报的电池事件,向上调用去计算电池,电量,使用等相关信息,它通过一个阻塞式的死循环不断监听底层三个文件节点上的事件信息,当监听到事件便调用到执行更新操作,通过.java中注册监听电池属性改变的函数,当电池属性信息发生改变,即回调到中做更新操作,更新完成一次电池事件的上报到更新整个流程就完成;总之是连接模块中java层与HAL层交互的主要通道

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了