Battery监听流程

[偶现]手机连接充电器后拔出,偶现拔出充电器后仍显示充电状态

ps healthd进程信息如下:

ps -ef |grep healthd
shell 17132 17126 15 11:41:11 pts/0 00:00:00 grep healthd
healthd进程号为17132,正常healthd的进程号不应该这么大,推断healthd发生过重启

由于在BatteryService.onStart中只向healthd中注册过一次监听,当healthd发生重启,并没有再次注册的动作
所以当healthd重启后,上层就收不到通知了

复现:
使用usb连接电脑, 使设备处于充电状态,先观察healthd的进程号,然后执行kill命令将healthd杀掉,再次观察healthd的进程号
然后断开usb连接,可以复现该问题

目前修改方案:

当healthd挂掉后,需要通知上层再次注册监听,需要利用死亡通知导入binder重连方案,已达到再次注册的目的
使用DeathRecipient进行binder重连。

Systemserver启动关键services

SystemServer是由系统启动的时候zygote启动的第一个java程序,三个启动服务方法按优先级先后启动(这个只是一个时间先后加载的过程与哪个服务优先级高关系不是太大),BatteryService在CoreServices中启动。

BatteryProperties回调流程图

BatteryService主要实现的功能 监听底层上报的battery事件,广播电池发生改变的消息。

 在BatteryService的onStart方法中向电源属性服务注册一个回调接口, 当电源属性发生变化时,BatteryListener的batteryPropertiesChanged函数将被调用

    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.

        }

        mBinderService = new BinderService();

        publishBinderService("battery", mBinderService);

        publishLocalService(BatteryManagerInternal.class, new LocalService());

     }

 监听类中,BatteryListener是一个Binder服务类,因此batteryproperties服务可以通过它传递数据。  

 电池数据的更新主要通过uevent监听kernel的节点信息变化来触发回调接口。

在system/core/healthd/BatteryMonitor.cpp中

bool BatteryMonitor::update(void) {

           ......

           healthd_mode_ops->battery_update(&props);

           ........

}

battery_update在Healthd.cpp中转换为healthd_mode_android_battery_update

static struct healthd_mode_ops android_ops = {

    .init = healthd_mode_android_init,

    .preparetowait = healthd_mode_android_preparetowait,

    .heartbeat = healthd_mode_android_heartbeat,

    .battery_update = healthd_mode_android_battery_update,

};

system/core/healthd/healthd_mode_android.cpp此处调用

BatteryPropertiesRegistrar的notifyListeners通知props改变了。

void healthd_mode_android_battery_update(

    struct android::BatteryProperties *props) {

    if (gBatteryPropertiesRegistrar != NULL)

        gBatteryPropertiesRegistrar->notifyListeners(*props);



    return;

}

在notifyListeners里面实现batteryPropertiesChanged回调接口,实现IBatteryPropertiesListener接口的batteryPropertiesChanged回调方法即可获取更新的电池参数。

system/core/healthd/BatteryPropertiesRegistrar.cpp

void BatteryPropertiesRegistrar::notifyListeners(const struct BatteryProperties& props) {

    Vector<sp<IBatteryPropertiesListener> > listenersCopy;

    {

        Mutex::Autolock _l(mRegistrationLock);

        listenersCopy = mListeners;

    }

    for (size_t i = 0; i < listenersCopy.size(); i++) {

        listenersCopy[i]->batteryPropertiesChanged(props);

    }

}

在BatteryService的onStart()中注册batteryPropertiesRegistrar 监听BatteryListener 然后update电池信息。

当电源属性发生变化后,回调BatteryService的update函数。

    private final class BatteryListener extends IBatteryPropertiesListener.Stub {

        @Override public void batteryPropertiesChanged(BatteryProperties props) {

            final long identity = Binder.clearCallingIdentity();

            try {

                BatteryService.this.update(props);

            } finally {

                Binder.restoreCallingIdentity(identity);

            }

       }

}

再往下看update做了什么操作,从代码来看mUpdatesStopped默认为false,通过shell command才有可能改变,最重要的是processValuesLocked更新电源相关的信息。

private void update(BatteryProperties props) {

        synchronized (mLock) {



            if (!mUpdatesStopped) {

                mBatteryProps = props;

                // Process the new values.

                processValuesLocked(false);

            } else {

                mLastBatteryProps.set(props);

            }

        }

    }

processValuesLocked方法主要是将电池信息封装进Intent然后以broadcastStickyIntent形式发送。

private void processValuesLocked(boolean force) {

    .......

        final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);

        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY

                | Intent.FLAG_RECEIVER_REPLACE_PENDING);

       .......

       .......

intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);

intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);

intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);

       ........

        mHandler.post(new Runnable() {

            @Override

            public void run() {

                ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);

            }

        });

        .............



}

如下图是从网络上摘取的图片

Uevent是内核通知android有状态变化的一种方法,比如USB线插入、拔出,电池电量变化等等。其本质是内核发送(可以通过socket)一个字符串,应用层(android)接收并解释该字符串,获取相应信息。

Battery Healthd进程

BatteryService中使用的batteryproperties服务位于healthd守护进程中,本层在Android中属于Native层,负责监听Kernel中上报的uevent,对电池电量进行实时监控。

healthd是安卓4.4之后提出来的,监听来自kernel的电池事件,并向上传递电池数据给framework层的BatteryService。BatteryService计算电池电量显示,剩余电量,电量级别等信息,其代码位于/system/core/healthd。

init.rc中

service healthd /system/bin/healthd

    class core

    critical

group root system wakelock

epoll机制

epoll_create、epoll_ctl、epoll_wait、close用法详解

Linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,linux/posix_types.h头文件有这样的声明:
#define__FD_SETSIZE   1024
         表示select最多同时监听1024个fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。

epoll的接口非常简单,一共就三个函数:

  1. 创建epoll句柄
       int epfd = epoll_create(intsize);     

 创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
函数声明:int epoll_create(int size)
该函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个epoll fd上能关注的最大socket fd数。随你定好了。只要你有空间。可参见上面与select之不同。

2.将被监听的描述符添加到epoll句柄或从epool句柄中删除或者对监听事件进行修改。

函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。
参数:
epfd:由 epoll_create 生成的epoll专用的文件描述符;
op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除
fd:关联的文件描述符;
event:指向epoll_event的指针;
如果调用成功返回0,不成功返回-1

   int epoll_ctl(int epfd, intop, int fd, struct epoll_event*event); 

   epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。

           第一个参数是epoll_create()的返回值,

           第二个参数表示动作,用三个宏来表示:
           EPOLL_CTL_ADD:       注册新的fd到epfd中;
          EPOLL_CTL_MOD:      修改已经注册的fd的监听事件;
           EPOLL_CTL_DEL:        从epfd中删除一个fd;
          第三个参数是需要监听的fd,

          第四个参数是告诉内核需要监听什么事件

3.等待事件触发,当超过timeout还没有事件触发时,就超时。

  int epoll_wait(int epfd, struct epoll_event * events, intmaxevents, int timeout);
    等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大(数组成员的个数),这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。

    该函数返回需要处理的事件数目,如返回0表示已超时。

    返回的事件集合在events数组中,数组中实际存放的成员个数是函数的返回值。返回0表示已经超时。
函数声明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)

BatteryProperties监听更新流程图

先来看healthd_init函数

static int healthd_init() {

    epollfd = epoll_create(MAX_EPOLL_EVENTS);创建epoll的fd

    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();//alarm初始化

    uevent_init();//uevent初始化

    gBatteryMonitor = new BatteryMonitor();

    gBatteryMonitor->init(&healthd_config);

    return 0;

}

uevent_init函数,主要是通过uevent_open_socket函数创建netlink socket。

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, EVENT_WAKEUP_FD))//注册到epoll中去

        KLOG_ERROR(LOG_TAG,

                   "register for uevent events failed\n");

}

将ueventd的fd加入到epoll中,当有数据过来就调用uevent_event处理函数。

int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {

    struct epoll_event ev;

    ev.events = EPOLLIN;

    if (wakeup == EVENT_WAKEUP_FD)

        ev.events |= 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;

}

uevent_event函数处理uevent事件

#define UEVENT_MSG_LEN 2048

static void uevent_event(uint32_t /*epevents*/) {

    char *cp;

    int n;

    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);//接受netlink socket消息

    ........

    while (*cp) {

        if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {

            healthd_battery_update();//更新电池信息

            break;

        }

        /* advance to after the next \0 */

        while (*cp++)

            ;

    }

    ......

}

在system/core/libcutils/uevent.c中uevent_kernel_multicast_recv的具体实现为

 /**

 * Like recv(), but checks that messages actually originate from the kernel.

 */

ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length)

{

    uid_t uid = -1;

    return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);

}

通过层层传递

 uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid)---->>

 uevent_kernel_recv(int socket, void *buffer, size_t length, bool require_group, uid_t *uid)---->>

 

epoll的主函数在healthd_mainloop中,epoll_wait等待事件过来,有事件过来就调用响应的处理函数。

static void healthd_mainloop(void) {

 .......

 while (1) {

        struct epoll_event events[eventct];

        int timeout = awake_poll_interval;

        int mode_timeout;

        ......

        mode_timeout = healthd_moepollfdde_ops->preparetowait();

        if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))

            timeout = mode_timeout;

        nevents = epoll_wait(, events, eventct, timeout);

        .....

        for (int n = 0; n < nevents; ++n) {

            if (events[n].data.ptr)

                (*(void (*)(int))events[n].data.ptr)(events[n].events);

        }

        ........



}

healthd_battery_update();函数里调用了 gBatteryMonitor->update()方法去完成实际意义上的更新

/system/core/healthd/healthd_common.cpp中,获取新的wakealarm唤醒间隔,fast wake处于充电模式,slow是处于非充电模式的唤醒间隔。

void healthd_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;

}

判断对应的event有没有相应的处理函数;uevent_fd的处理函数是uevent_event,wakealarm_fd的处理函数是wakealarm_event,

gBindfd,uevent_fd,wakealarm_fd注册监听都是通过healthd_register_event函数实现,然后将其三个文件节点加入到epollfd中。

在healthd_mode_android.cpp中

gBindfd的处理函数是binder_event,binder_event事件注册到gBinderfd文件节点用以监听Binder事件。

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(gBatteryPropertiesRegistrar);

}

下图摘自网络

 

问:android怎么知道当前是什么供电,充电中?

答:uevent机制(实质是net_link方式的socket)(广泛应用于hotplug),充电插入与断开时,内核通过发送uevent信息,告诉android。

问:android如何知道各种参数并更新的?

答:通过kobject_uevent发送通知给上层,上层读取sys相关文件属性

Kernel层

内核中使用的是power_supply框架,对芯片驱动填充好power_supply结构体,再进行注册即可。

kernel-4.4/drivers/power/power_supply_core.c

主要提供psy的注册/注销,psy基本属性的设置(调用的是注册时传入的函数指针回调进行设置),还有供电改变power_supply_changed发送kobject_uevent给android的HAL层。

kernel-4.4/drivers/power/power_supply_sysfs.c

负责sysfs文件系统下的属性文件,主要负责,power_supply下的各种属性结点的show和store,还有填充uevent的信息。

kernel-4.4/drivers/power/power_supply_leds.c

提供了充电,点亮满等trigger的注册,还有根据电池不同状态和容量时,执行LED动作。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值