Android 内核学习之三-----Power源码分析学习(3)
来源:互联网 发布:源码生成apk 编辑:程序博客网 时间:2024/05/16 18:13
Android 内核学习之三-----Power源码分析学习(3)
5. Healthd学习
(1)healthd概况
healthd是android4.4以后,google专门提供用来监控电池信息变化。文件路径为:
XXXandroid/system/core/healthd/htalthd.cpp,XXXandroid/system/core/healthd/htalthd.h
包含的文件路径为:
XXXandroid/system/core/healthd/BatteryMonitor.h
XXXandroid/system/core/healthd/BatteryMonitor.cpp
XXXandroid/system/core/healthd/BatteryPropertiesRegistrar.h
XXXandroid/system/core/healthd/BatteryPropertiesRegistrar.cpp
./frameworks/native/include/batteryservice/ IBatteryPropertiesRegistrar.h
./frameworks/native/include/batteryservice/ IBatteryPropertiesRegistrar.cpp
./frameworks/native/include/batteryservice/IBatteryPropertiesListener.h
./frameworks/native/include/batteryservice/IBatteryPropertiesListener.cpp
batteryservice/BatteryService.h
(2)healthd->main
healthd_board_init(&healthd_config);
wakealarm_init();
uevent_init();
binder_init();
gBatteryMonitor= new BatteryMonitor();
gBatteryMonitor->init(&healthd_config, nosvcmgr);
healthd_mainloop();
上图为main方法中的核心代码,首先的XXX_init是初始化操作,然后是初始化BatteryMonitor,最后执行healthd_mainloop(),该方法看名字应该是一个死循环然后在循环里做各种操作了。
(3)healthd->uevent_init
static void uevent_init(void) {
uevent_fd = uevent_open_socket(64*1024, true);
if (uevent_fd >= 0)
fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
else
KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
}
上图为uevent_init的定义,其中uevent_fd = uevent_open_socket(64*1024, true); 用于创建NETLINK socket,用于监听内核发送过来的uevent消息 。
(4)BatteryMonitor::init
DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
if (dir == NULL) {
KLOG_ERROR(LOG_TAG,"Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
} else {
struct dirent* entry;
while ((entry = readdir(dir))) {
const char* name = entry->d_name;
if (!strcmp(name, ".") || !strcmp(name,".."))
continue;
char buf[20];
// Look for "type" file in each subdirectory
path.clear();
path.appendFormat("%s/%s/type",POWER_SUPPLY_SYSFS_PATH, name);
switch(readPowerSupplyType(path)) {
Init的方法主要是初始化mHealthdConfig结构体中的属性,这些属性对应的值如果有变化将通知到客户端去。
首先通过opendir(POWER_SUPPLY_SYSFS_PATH)方法,打开POWER_SUPPLY_SYSFS_PATH,而POWER_SUPPLY_SYSFS_PATH就是”power_supply”,这里打开的就是/sys/class/power_supply这个目录。
然后while((entry = readdir(dir)))就是通过读取该目录下的所有属性值挂载到mHealthdConfig结构体中。读取属性值的方法就是先读取目录中对应的变量的名称,再通过map匹配的方式获得对应的宏定义。
(5)healthd->healthd_mainloop();
while (1) {
structepoll_event events[maxevents];
intnevents;
IPCThreadState::self()->flushCommands();
nevents =epoll_wait(epollfd, events, maxevents, awake_poll_interval);
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(*)())events[n].data.ptr)();
}
if(!nevents)
periodic_chores();
}
healthd_mainloop的核心代码就是这个while(1)的循环。循环代码很简单,就是通过epoll来监听上面提到的socket发生的操作。通过epoll_wait轮询检查socket上是否有事情发生,如果有时间发生,那么执行(*(void (*)())events[n].data.ptr)();。而.data.ptr我们关注的挂载是uevent_event,uevent_event定义在healthd.cpp中
(6)healthd->uevent_event
#define UEVENT_MSG_LEN 1024
static void uevent_event(void) {
char msg[UEVENT_MSG_LEN+2];
char *cp;
int n;
n = uevent_kernel_multicast_recv(uevent_fd, msg,UEVENT_MSG_LEN);
/*judge where the socket come from*/
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)) {
battery_update();
break;
}
/* advance to after the next \0 */
while (*cp++)
;
}
其中n = uevent_kernel_multicast_recv(uevent_fd,msg, UEVENT_MSG_LEN);用于判断消息来自哪里。获取到msg以后,通过后面的strcmp与” SUBSYSTEM=power_supply”进行比较,如果相等,那么可以确定是来自电源管理的消息,则继续执行battery_update()。battery_update()定义在healthd中。
(7)healthd-> battery_update
static void battery_update(void) {
// Fast wake interval when on charger (watch for overheat);
// slow wake interval when on battery (watch for drained battery).
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);
// During awake periods poll at fast rate. If wake alarm is set at fast
// rate then just use the alarm; if wake alarm is set at slow rate then
// poll at fast rate while awake and let alarm wake up at slow rate when
// asleep.
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;
}
这就是上面回调的方法,其中最重要的是在这个方法里面调用了gBatteryMonitor->update(),这个方法就是真正用来向上层通知电源消息变化的。
(8)BatteryMonitor::update
这个方法主要是用来填充BatteryProperties这个结构体的。
structBatteryProperties {
bool chargerAcOnline;
bool chargerUsbOnline;
bool chargerWirelessOnline;
int batteryStatus;
int batteryHealth;
bool batteryPresent;
int batteryLevel;
int batteryVoltage;
int batteryCurrentNow;
int batteryChargeCounter;
int batteryTemperature;
String8 batteryTechnology;
status_t writeToParcel(Parcel* parcel)const;
status_t readFromParcel(Parcel* parcel);
};
而填充的过程是从”/sys/class/power_supply/XXX”目录中读取相关的变化值填充到该结构体中。上述的目录就是在BatteryMonitor执行init方法的时候初始化的。其实BatteryMonitor的类中最重要的就两个函数init()和update(),其他的函数都是为这两个方法服务。另外还有一个重要的变量,该变量就是在init中初始化的mBatteryPropertiesRegistrar,该变量用来将变化的信息(变化的信息填充到了上述的结构体BatteryProperties中)发送到更顶层中去。
if (mBatteryPropertiesRegistrar != NULL)
mBatteryPropertiesRegistrar->notifyListeners(props);
上述代码就是通过mBatteryPropertiesRegistrar的notifyListeners方法将BatteryProperties发送出去。下面从mBatteryPropertiesRegistrar的初始化讲起,看看填充了电源的信息结构体BatteryProperties是怎么被发送出去的。
(9)BatteryPropertiesRegistrar::publish()
在BatteryMonitor的init中,最后调用了BatteryPropertiesRegistrar的构造函数,将BatteryMonitor对象设置到BatteryPropertiesRegistrar的monitor中,同事调用了publish方法。
void BatteryPropertiesRegistrar::publish() {
defaultServiceManager()->addService(String16("batterypropreg"),this);
}
BatteryPropertiesRegistrar::publish()的方法很简单,就是将BatteryPropertiesRegistrar添加到服务中。这个地方就有点似曾相识了,在学习binder的时候,我们知道服务端的服务就是通过这种方式加入到service中的。
我们继续看代码,发现BatteryPropertiesRegistrar确实继承自Ibinder了,而且是BnBatteryPropertiesRegistrar。对Binder熟悉的应该知道BnXXX服务就是本地真正的实现服务了。客户端通过BpXXX方法最终调用到BnXXX的方法实现了进程间的通信。继续看代码,看看BnBatteryPropertiesRegistrar给BpBatteryPropertiesRegistrar提供了一些什么服务。
(10)BatteryPropertiesRegistrar.h
在BatteryPropertiesRegistrar.h中我们看到BatteryPropertiesRegistrar继承自BnBatteryPropertiesRegistrar和Ibinder。并且定义了如下几个方法:
从上图可以看到public中包括构造函数有三个方法,private中也有三个方法。public中的方法是BatteryPropertiesRegistrar其自身的成员函数,其中publish我们上文已经分析过,而notifyListeners将在下面分析。然后private中的方法registListener和unregistListener继承自BnBnBatteryPropertiesRegistrar了。也就是说BnBnBatteryPropertiesRegistrar中的这两个方法实现是在这里实现的。通过名字我们就可以再知道,这两个方法是注册listener的方法。BnBnBatteryPropertiesRegistrar的定义我们要去IBatteryPropertiesListener.h中去查看,BpBnBatteryPropertiesRegistrar的定义在IBatteryPropertiesListener.cpp中。
(11)IBatteryPropertiesRegistrar.h
IBatteryPropertiesRegistrar首先定义了IBatteryPropertiesRegistrar,继承自IInterface。而BnXX和BpXXX都是继承自IXXX的,因此,这里的IBatteryPropertiesRegistrar是后面BnBatteryPropertiesRegistrar和BpBatteryPropertiesRegistrar父类(或者说是他们需要实现的接口类)。
从上面代码可以看出IXXX中定义了两个方法,而BnXXX有继承了IXXX,同时定义了一个onTransact的方法,这个方法是Binder机制中用于客户端线程和服务端线程通信的方法。直观的说明就是BpXXX也通过调用onTransact方法,会将方法名和参数传入的Binder驱动的后台,然后经过Binder机制的转换可以调用到BnXXX中的onTransact。BnXXX中的onTransact可以通过传过来的值做真正的处理,然后将处理之在通过相同的方式返回到BpXXX中。在这里肯定就是BpBatteryPropertiesRegistrar调用registListener和unregistLintener两个方法了。我通过后面的代码分析。
(12)IBatteryPropertiesRegistrar.cpp
IBatteryPropertiesRegistrar.cpp中实现了BpBatteryPropertiesRegistrar同时实现了BnBatteryPropertiesRegistrar中的onTranscat方法。
如上面代码所示BpBatteryPropertiesRegistrar中也定义了registListener和unregisterLintener,但是其具体的实现中调用了transact方法,而transact方法最终会调用BnBatteryPropertiesRegistrar的onTransact方法。其具体实现如下:
在onTransact方法中会根据code执行registListener和unregisterListener,而这两个方法都是在BnBatteryPropertiesRegistrar中定义在BatteryPropertiesRegistrar.cpp中实现的。
到此我们终于转回去了,终于BatteryPropertiesRegistrar中去了。尽管转回去了,我们这里还是要分两路来继续看代码了。首先一路是,我们转到BatteryPropertiesRegistrar中去,继续分析后面的registListener、unregistListener和notifyListener。另一路是,既然BnBatteryPropertiesRegistrar作为客户端进程了,那么是哪一个客户端来调用BnBatteryPropertiesRegistrar这里面的registListener和unRegistListener的方法呢。这个我们后续将一一说明。
(13)BatteryPropertiesRegistrar::registerListener
RegisterListener方法很简单,就是往listener中添加listener对象。代码如下:
方法的最后是执行了BatteryMonitor中的update,这个方法我们前面提到过,但是碰到了notifyListenr我们就分析了上面的一大堆内容了,现在又回到这里了,我们继续仅投入到BatteryMonitor的update中去。
(14)BatteryMonitor::update
Update方法我们前面讲过,主要是填充BatteryProperties结构体,然后将该结构体的信息通过notifyListener传到上层去。其主要代码如下:
红线上部分省略了大部分的填充代码,下部分就是notify了,而参数props就是填充的BatteryProperties结构体。我们跳转到notifyListeners中去分析。notifyListeners定义在BatteryPropertiesRegistrar中。
(15)BatteryPropertiesRegistrar::notifyListeners
notifyListeners代码也比较简单,代码如下:
这个代码虽然简单,但是这里是一个结点,是众多类或者对象在这里的集结于分发的结点。首先我们看到代码里调用了batteryPropertiesChanged的函数,这个函数肯定是listener的一个成员函数,但是这个listener是在哪里定义呢?这个就是我们前面讲到registerListener的时候讲到的另一路了。这里我们就暂时放下batteryPropertiesChanged方法,去分析上面留下的另一路问题:在哪个客户端线程中调用了BpBatteryPropertiesRegistrar的registerListener和unregisterListener方法。
(16)IBatteryPropertiesRegistrar.aidl
找了一圈没找到jni或者cpp的文件直接调用registerListener了,只有这里通过aidl方式进行调用。AIDL就是android接口定义语言,是android固定的格式。在AIDL文件中定义好接口,就可以直接生成一个接口类,接口类中有哟个stub类和一个proxy类。其中stub类类似于binder中的服务端,proxy相当于binder中的客户端,一般app层获取服务中的proxy对象,然后执行proxy对象的方法时候就是间接调用stub中的方法,一般stub中的方法实现了AIDL中的接口,大多数是通过native的jni调用。但是在这里没找到jni入口,不知道这里是怎么调用的底层BpRegisterXXX中的registListener和unregisterListener方法的,但是入口在这里是做不了的。这里作为一个疑问有待后续继续深入学习了。
Aidl的接口定义格式如下:
(17)IBatteryPropertiesRegistrar.java
这个接口就是通过上述的AIDL生成的,里面的内容是固定的,一般不需要更改。我们可以看到里面有两个实现了接口的子类stub和proxy。代码省略。客户端一般调用proxy的方法,我们看看proxy中的registListener()方法的调用是在BatteryService中。
(18)com.android.server.BatteryService
从上述代码可以看出先通过ServiceManager获取到Binder,其中batterypropreg就是我们前面讲述的在BatteryPropertiesRegistrar中通过publish,在底层注册的BnBatteryPropertiesRegistrar。通过binder获取到的mBatteryPropertiesRegistrar其实就是IBatteryPropertiesRegistrar中的proxy了。
而通过registListener方法注册的mBatteryPropertiesListener是BatteryListener的对象。我们继续看BatteryListener的代码。该类的定义也在BatteryService.java中。
(19)com.android.server.BatteryService$BatteryListener
从上述代码我们看到BatteryListener继承自IBatteryListener.stub。这个地方很明显又是一个Binder机制的内容。我们这里放下内部代码的分析先跳转到IbatteryPropertiesListener的分析。
(20)IBatteryPropertiesListener.aidl
Aidl文件中只有一个借口函数,就是我们在notifyListener中提到的无处安放的“BatterypropertiesListenerChanged”。然后我们继续找这个aidl文件生成的接口类。
(21)android.os.IBatteryPropertiesListener.java
这个文件中也是一样,定义了一个继承自Binder的接口IBatteryPropertiesListener。然后生成了两个实现了接口的类stub和proxy。Proxy的方法给客户端调用,最终会调用到作为服务端的stub方法。而服务端的方法是真正的去实现了方法的操作。在这个接口中只有一个BatterypropertiesListenerChanged方法。真正的实现是在上文中提到的com.android.server.BatteryService$BatteryListener中。BatteryListener继承了IBatteryPropertiesListener.Stub,实现了BatterypropertiesListenerChanged方法。怎么实现的我们待会再讲,现在进入到注册Listener的方法中继续分析,这里的listener是怎么跟notifyListener方法中的listener关联起来的。所以再回到com.android.server.BatteryService中。
(22)com.android.server.BatteryService
==============================================
上述两端代码可以看出,registerListener中注册了一个IBatteryPropertiesListener.stub的子类,即BatteryListener。也就是注册了一个BnXXX类。到这里我们还要回到BatteryPropertiesRegistrar中去分析代码。
(23)registerListener()和notifyListener()
这两个方法是定义在BatteryPropertiesRegistrar中。
上面代码中的constsp<IBatteryPropertiesListener>& listener其实是一个Bp IBatteryPropertiesListener了,这其中怎么转的还要看Binder机制。(我记得是这样,可能记忆有偏差)总之这里是将前面的stub所代表的Binder子类转换成了BpXXX的了。
然后执行mListener[i]-> batteryPropertiesChanged方法的时候,其实是执行了BpIBatteryPropertiesListener中的batteryPropertiesChanged,通过前面我们了解到的Binder机制可知,最终会调用到BnIBatteryPropertiesListener中的方法了。而BnIBatteryPropertiesListener在上面的IBatteryPropertiesListener.java分析可知,最终是在BatteryListener中实现了batteryPropertiesChanged。所以我们可以很清楚了,这里的batteryPropertiesChanged真正的实现要去BatteryService中的内部类BatteryListener中去查看分析。
(24)BatteryService$BatteryListener.batteryPropertiesChanged
从上面可以看出batteryPropertiesChanged方法调用了batteryService中的update方法了,我们跳到update中去。
(25)BatteryService.update
Update又调用了processValuesLocked()方法,在跳转到processValuesLocked中去。
(26)BatteryService.processValuesLocked
这个函数代码比较长,但是里面的逻辑很简单,首先是解析底层传上来的那个结构体中的信息,然后跟当前信息比较,如果某个属性的值发生了变化就发送一个UserHandle.ALL的广播,最后再把最新的值保存到本地变量中,以便下次有新的结构体消息上来时候作比较。
(27)小结
绕来绕去绕了这么久终于把这个流程绕完了,总体来说涉及的内容还是挺多的。我们简单把流程在过一下。
首先是驱动层,有两个驱动。一个是电池本身的驱动,一个是消息传递的power_supply驱动。电池驱动就是硬件驱动,消息驱动power_supply采用的是uevent机制。Uevent机制将电源变化的属性信息通过socket发送到文件目录sys/class/power_supply下。
然后,电源管理在android4.4以后采用了healthd的方式。Healthd的方式通过epoll的方式不断的去轮询sys/class/power_supply下的的属性值是否有变化。如果变化则通过notify的机制通知到BatteryService中,BatteryService通过广播机制给UserHandler.ALL广播。
2. 总结
终于算是走完了整个流程了,尽管中间还有很多细节不是很明白,也没有花时间去搞的很透彻,但是对于自己来说已经算是一种小小的胜利了。上面的分析是按照功能模块一块块来分析的,并没有对各个部分代码属于Android系统的哪层做一个划分,这个应该是一个欠缺,也可能是某些没搞明白地方的症结点。比如在上面的aidl生成的接口类中,接口方法是怎么跟底层交互的。这里面没看到jni方法,没看到native定义,所以这里还是有某些环节是欠缺的。这个留待以后再去深入的学习学习。还有一个问题是Binder机制还不能信手拈来,虽然Binder这块内容看了很多遍,但是总有一些胆怯在里面,碰到Binder就会觉着这个是个难缠的家伙。后面还要时间去多熟悉熟悉Binder。
这个模块前前后后看了差不多一个月的时间了,虽然脚步很慢,但是还是一步一步的往上爬。
继续努力,Keep moving!!!
- Android 内核学习之三-----Power源码分析学习(3)
- Android内核学习之三----------Power源码分析学习(1)
- Android 内核学习之三-----Power源码分析学习(2)
- spring源码学习之三 XmlWebApplicationContext.loadBeanDefinitions源码分析
- Android FM模块学习之四源码解析(三)
- tomcat源码分析学习笔记(三)
- Android FM模块学习之四源码分析(五)
- Android FM模块学习之源码分析(六)
- Android FM模块学习之四源码分析(七)
- Android FM模块学习之四源码分析(八)
- Vue学习之源码分析--从Vue.js源码角度再看数据绑定(三)
- WebKit内核学习之三
- Linux内核源码学习之 内核编译
- Android 5.0内核和源代码学习(2)——源码下载和系统启动过程分析
- 内核源码学习:段机制和描述符(三)
- 《STL源码剖析》学习--6章--power算法分析
- KVM内核源码学习之源码组成
- Power Shell 学习笔记(三)变量
- 关于在Eclipse中使用命令行的问题
- 我又回来了!
- poj3984迷宫问题【bfs+记录路径】
- linux下的Makefile详解(4)
- noip1996 字符串编辑 - 普及组 (模拟,字符串处理)
- Android 内核学习之三-----Power源码分析学习(3)
- poj 3278 Catch That Cow(BFS)
- C# MemoryStream和BinaryFormatter
- Service生命周期初步了解
- java图片截取上传
- 南邮 OJ 1625 素数判定问题
- PDF之itextsharp的使用开发历程1
- 【采集层】Kafka 与 Flume 如何选择
- javascript闭包理解