一.初始化
1.设置关机充电的healthd_mode_ops → charger_ops
2 . 创建epoll的fd,创建关机闹钟的处理线程(创建一个新的epoll,并让关机闹钟处理事件epoll_wait在上面,如果关机闹钟事件触发,那么直接以rtc的开机原因热重启)
3 . 发送电池相关信息给到快充驱动
a)读取"/persist/bms/batt_info.txt"中的电池相关信息,如果没有该文件则创建此文件;
100:0:8:280:2674000 电池百分比:电池内阻阻抗:电池电压(V) : 温度 : 电池充满的状态
最后一个参数来自于:/sys/class/power_supply/battery/charge_full
b)逐个将上面的信息写入"/sys/class/power_supply/bms/battery_info_id"和"/sys/class/power_supply/bms/battery_info"需要留意的是这2个节点可能没有
c)读取快充驱动"/sys/class/power_supply/bms/soc_reporting_ready"的状态,直到驱动状态为ready后,向"/sys/class/power_supply/bms/battery_info_id"和 "/sys/class/power_supply/bms/battery_info"写入对应的值,返回。但是如果"/sys/class/power_supply/bms/soc_reporting_ready"这个节点内核并没有提供,则直接返回。
4 . 调用charger_ops提供的init函数完成charger相关初始化
a)到这一步,必须保证驱动的charger使能是打开的,否则无法继续下去,代码会判断"/sys/class/power_supply/battery/charging_enabled"的状态,如果不是enabled状态会直接重启机子。
b)监听按键(例如音量键,电源键)
c)初始化关机动画的资源文件,创建surface;
5 . 设置定时刷新电池电量的闹钟(默认10 min刷新一次)
6 . 设置接收的uevent事件(内核通过uevent主动上报电量)
7 . 通过BatteryMonitor初始化healthd_config指向的电源相关各个设备节点的路径
8 . 进入healthd_mainloop,等待事件发生
二.动画显示过程
对于动画,每轮显示会进入update_screen_state
多次,最后灭屏进入suspend,细分的话有2种场景:
batt_anim->num_cycles :充电完整动画重复显示的次数
batt_anim->num_frames :每次充电完整动画需要分解为几次显示
其中每显示一帧,都会进入update_screen_state
一次
1.按键唤醒或者插入充电器的时候,当前电量没到最后一个frame的范围,那么连续显示当前电量的frame到电量满的frame动画,并重复三次,其中每轮动画中,当前电池电量对应的frame显示1.5秒,除此之外的每个frame默认显示时间为750ms。
2. 按键唤醒或者插入充电器的时候,当前电量已经达到最后一个frame的范围,那么显示充电满的frame,同样连续显示3次,每次1.5s。
static void update_screen_state(struct charger *charger, int64_t now)
{
struct animation *batt_anim = charger->batt_anim;
int disp_time;
/*
1. batt_anim->run如果设置为false,则意味着不显示动画,就此返回
2. 如果当前时间小于下一次动画的触发时间,就此返回
*/
if (!batt_anim->run || now < charger->next_screen_transition) return;
//以下代码主要是用于初始化显示相关,因此只会初始化一次
if (!minui_inited) {
...
//初始化显示
gr_init();
//获取字符显示的长和宽
gr_font_size(gr_sys_font(), &char_width, &char_height);
//do't care it
init_status_display(batt_anim);
...
minui_inited = true;
}
/* animation is over, blank screen and leave */
//如果本轮显示的次数达到了num_cycles(默认3次,4.5秒),则清屏,关背光,机子进入suspend
if (batt_anim->num_cycles > 0 && batt_anim->cur_cycle == batt_anim->num_cycles) {
reset_animation(batt_anim);
charger->next_screen_transition = -1;
healthd_board_mode_charger_set_backlight(false);
gr_fb_blank(true);
LOGV("[%" PRId64 "] animation done\n", now);
if (charger->charger_connected)
request_suspend(true);
return;
}
//第一次进入,batt_anim->cur_frame是为0的,这儿是取出第0帧的图像需要显示的时间(750ms)
disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
/* animation starting, set up the animation */
//下面的逻辑还是比较简单的,根据从内核读到的电池百分比,选择当前应该显示的图片,并重新计算距离下次显示需要等待的最小时间间隔,这里需要特别留意的是,由于是第一帧因此显示时间是正常的2倍(1.5秒),之后的显示就没有这样的待遇了,只有750ms.
if (batt_anim->cur_frame == 0) {
LOGV("[%" PRId64 "] animation starting\n", now);
if (batt_prop) {
batt_anim->cur_level = batt_prop->batteryLevel;
batt_anim->cur_status = batt_prop->batteryStatus;
if (batt_prop->batteryLevel >= 0 && batt_anim->num_frames != 0) {
/* find first frame given current battery level */
for (int i = 0; i < batt_anim->num_frames; i++) {
if (batt_anim->cur_level >= batt_anim->frames[i].min_level &&
batt_anim->cur_level <= batt_anim->frames[i].max_level) {
batt_anim->cur_frame = i;
break;
}
}
// repeat the first frame first_frame_repeats times
disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
batt_anim->first_frame_repeats;
}
}
}
/* unblank the screen on first cycle */
//如果是本轮显示的第一次,则屏幕内容不清楚,同时点亮背光灯
if (batt_anim->cur_cycle == 0) {
gr_fb_blank(false);
healthd_board_mode_charger_set_backlight(true);
}
//开始显示动画
/* draw the new frame (@ cur_frame) */
redraw_screen(charger);
/* if we don't have anim frames, we only have one image, so just bump
* the cycle counter and exit
*/
//如果没有动画资源,则自增圈数,更新距离下次显示的最小时间间隔,然后就此返回
if (batt_anim->num_frames == 0 || batt_anim->cur_level < 0) {
LOGW("[%" PRId64 "] animation missing or unknown battery status\n", now);
charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
batt_anim->cur_cycle++;
return;
}
//这里更新距离下次显示的最小时间间隔
/* schedule next screen transition */
charger->next_screen_transition = now + disp_time;
/* advance frame cntr to the next valid frame only if we are charging
* if necessary, advance cycle cntr, and reset frame cntr
*/
//下面的逻辑是:只要充电的电量没有达到最后一个frame的阶段,那么直接取出下一次显示需要的frame用于显示。
if (charger->charger_connected) {
batt_anim->cur_frame++;
//如果当前的电量已经没有落在下一帧中,那么需要调整下一帧的动画到合理的位置
while (batt_anim->cur_frame < batt_anim->num_frames &&
(batt_anim->cur_level < batt_anim->frames[batt_anim->cur_frame].min_level ||
batt_anim->cur_level > batt_anim->frames[batt_anim->cur_frame].max_level)) {
batt_anim->cur_frame++;
}
//如果当前已经是最后一帧,那么将下一帧重置为0
if (batt_anim->cur_frame >= batt_anim->num_frames) {
batt_anim->cur_cycle++;
batt_anim->cur_frame = 0;
/* don't reset the cycle counter, since we use that as a signal
* in a test above to check if animation is over
*/
}
} else {
/* Stop animating if we're not charging.
* If we stop it immediately instead of going through this loop, then
* the animation would stop somewhere in the middle.
*/
batt_anim->cur_frame = 0;
batt_anim->cur_cycle++;
}
}
三.用户按下电源键的处理流程
还记得上一步中我们已经注册了按键的监听如下:
ret = ev_init(input_callback, charger);
if (!ret) {
epollfd = ev_get_epollfd();
healthd_register_event(epollfd, charger_event_handler, EVENT_WAKEUP_FD);
}
简单说明一下,上面也创建了一个epoll,ev_get_epollfd
是返回这个epoll的fd,之后将这个epoll的fd放到了healthd里面的healthd_mainloop中的epoll中统一进行监听。
一旦按键事件被触发:
charger_event_handler
->ev_dispatch
->input_callback
->update_input_state
->set_key_callback
如下:
static int set_key_callback(int code, int value, void *data)
{
struct charger *charger = (struct charger *)data;
int64_t now = curr_time_ms();
int down = !!value;
if (code > KEY_MAX)
return -1;
/* ignore events that don't modify our state */
if (charger->keys[code].down == down)
return 0;
/* only record the down even timestamp, as the amount
* of time the key spent not being pressed is not useful */
if (down)
charger->keys[code].timestamp = now;
charger->keys[code].down = down;
charger->keys[code].pending = true;
if (down) {
LOGV("[%" PRId64 "] key[%d] down\n", now, code);
} else {
int64_t duration = now - charger->keys[code].timestamp;
int64_t secs = duration / 1000;
int64_t msecs = duration - secs * 1000;
LOGV("[%" PRId64 "] key[%d] up (was down for %" PRId64 ".%" PRId64 "sec)\n",
now, code, secs, msecs);
}
return 0;
}
很简单的逻辑将按键的code码和按下和抬起的状态,pending状态存入charger->keys中
由于按键唤醒了healthd进程,处理完按键事件后,会再次调用healthd_mode_ops->heartbeat();之后再进入poll_wait状态
void healthd_mode_charger_heartbeat()
{
struct charger *charger = &charger_state;
int64_t now = curr_time_ms();
handle_input_state(charger, now);
handle_power_supply_state(charger, now);
/* do screen update last in case any of the above want to start
* screen transitions (animations, etc)
*/
update_screen_state(charger, now);
}
我们关注针对按键的操作:handle_input_state->process_key(charger, KEY_POWER
, now)
static void process_key(struct charger *charger, int code, int64_t now)
{
struct animation *batt_anim = charger->batt_anim;
struct key_state *key = &charger->keys[code];
if (code == KEY_POWER) {
if (key->down) {
int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME;
//如果上次电源键按下设置的2s超时时间到了,同时当前的按键依然是按下状态,那么我们判定用户是希望重启系统而不是点亮屏幕继续保持关机充电。
if (now >= reboot_timeout) {
/* We do not currently support booting from charger mode on
all devices. Check the property and continue booting or reboot
accordingly. */
if (property_get_bool("ro.enable_boot_charger_mode", false)) {
LOGW("[%" PRId64 "] booting from charger mode\n", now);
property_set("sys.boot_from_charger_mode", "1");
} else {
if (charger->batt_anim->cur_level >= charger->boot_min_cap) {
LOGW("[%" PRId64 "] rebooting\n", now);
android_reboot(ANDROID_RB_RESTART, 0, 0);
} else {
LOGV("[%" PRId64 "] ignore power-button press, battery level "
"less than minimum\n", now);
}
}
} else {
/* if the key is pressed but timeout hasn't expired,
* make sure we wake up at the right-ish time to check
*/
/*
* 第一次按下按键会先进入这儿
* 这里的作用就一个更新poll_wait的超时时间为2s
*/
set_next_key_check(charger, key, POWER_ON_KEY_TIME);
}
} else {
if (key->pending) {
/* If key is pressed when the animation is not running, kick
* the animation and quite suspend; If key is pressed when
* the animation is running, turn off the animation and request
* suspend.
*/
if (!batt_anim->run) {
//如果在上次按下电源键的2s时间内有抬起的动作,那么判定用户的意图只是希望看下当前充电的状态(电量)
kick_animation(batt_anim);
request_suspend(false);
} else {
//如果用户是在屏幕亮起的状态下按了电源键,我们认为用户是想要灭屏
reset_animation(batt_anim);
charger->next_screen_transition = -1;
healthd_board_mode_charger_set_backlight(false);
gr_fb_blank(true);
if (charger->charger_connected)
request_suspend(true);
}
}
}
}
key->pending = false;
}
如上,只有在按键抬起的时候,batt_anim->run的状态才会发生改变
kick_animation会将batt_anim->run置为true,从而使能动画显示。
之后在处理完本次按键事件后,会通过调用healthd_mode_ops->heartbeat()开启动画的显示,随后更新超时时间,进入三轮动画的显示流程。
四.内核主动通过uevent上报的事件处理过程
uevent_event
->healthd_battery_update
->BatteryMonitor->update()
->healthd_mode_charger_battery_update()
这个函数很简单,只是简单的将从内核获取的prop信息传递给batt_prop
void healthd_mode_charger_battery_update(struct android::BatteryProperties *props)
{
...
batt_prop = props;
...
}
随后代码流程会回到healthd_mainloop中
healthd_mainloop:
healthd_mode_ops->heartbeat();
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);
...
显然之后还会CALL到heartbeat,之后再更新epoll的超时时间
heartbeat在关机充电的时候指向healthd_mode_charger_heartbeat
void healthd_mode_charger_heartbeat()
{
struct charger *charger = &charger_state;
int64_t now = curr_time_ms();
//处理按键相关
handle_input_state(charger, now);
handle_power_supply_state(charger, now);
/* do screen update last in case any of the above want to start
* screen transitions (animations, etc)
*/
//刷新屏幕显示
update_screen_state(charger, now);
}
如上,如果是uevent事件,那么按键处理相关是不会触发的,同时动画相关的屏幕显示其实也不会触发。
原因在于batt_anim->run,并没有被置1。那么这里还有一种情况就是在三轮动画的显示流程中,如果有来自于内核的uevent电源事件到来怎么处理。
static void update_screen_state(struct charger *charger, int64_t now)
{
struct animation *batt_anim = charger->batt_anim;
int disp_time;
if (!batt_anim->run || now < charger->next_screen_transition) return;
...
}
如上,这种场景会由于当前时间小于下一次的frame显示时间而返回
五.插入usb进行关机充电,会直接显示三轮动画,原因如下:
main:
periodic_chores();
healthd_mode_ops->heartbeat();
关注这里的periodic_chores
periodic_chores
->healthd_battery_update
->BatteryMonitor::update
->healthd_mode_charger_battery_update
如下:
void healthd_mode_charger_battery_update(
struct android::BatteryProperties *props)
{
struct charger *charger = &charger_state;
charger->charger_connected =
props->chargerAcOnline || props->chargerUsbOnline ||
props->chargerWirelessOnline;
//第一次进入have_battery_state为false,之后一直为true
if (!charger->have_battery_state) {
charger->have_battery_state = true;
charger->next_screen_transition = curr_time_ms() - 1;
reset_animation(charger->batt_anim);
kick_animation(charger->batt_anim);
}
batt_prop = props;
}
通过kick_animation使能动画显示anim->run = true
之后再进入healthd_mode_ops->heartbeat();从而进入动画的显示流程