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!!!


1 0
原创粉丝点击