一.初始化

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_fullb)逐个将上面的信息写入"/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 itinit_status_display(batt_anim);...minui_inited = true;}/* animation is over, blank screen and leave *///如果本轮显示的次数达到了num_cycles(默认3次,4.5秒),则清屏,关背光,机子进入suspendif (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 timesdisp_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++;}//如果当前已经是最后一帧,那么将下一帧重置为0if (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 onall devices. Check the property and continue booting or rebootaccordingly. */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,之后一直为trueif (!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();从而进入动画的显示流程

Android 7.1关机充电流程相关推荐

  1. android 9.0关机充电流程,充电图标和电量显示百分比修改

    android 9.0关机充电图标和字体修改 相关源文件 电量显示百分比字体替换 充电图标替换 相关源文件 system/core/healthd/healthd_draw.cppsystem/cor ...

  2. android关机充电流程、充电画面显示

    一.Android正常开机流程.关机充电流程 在写这篇文章之前我们先看两个流程:正常开机流程,关机充电系统启动流程 1.正常开机流程,按开机键. 可大致分成三部分 (1).OS_level:UBOOT ...

  3. Android上电到现实充电画面,android 电池(二):android关机充电流程、充电画面显示(一)...

    上一篇我们讲了锂电池的充放电的流程和电池的一些特性,这一节我们重点说一下android关机充电是怎么.充电画面显示是怎么实现的,这个在工作中也比较有用,我们开始做这一块的时候也走了不少的弯路.我记得我 ...

  4. android 电池(二):android关机充电流程、充电画面显示

    上一篇我们讲了锂电池的充放电的流程和电池的一些特性,这一节我们重点说一下android关机充电是怎么.充电画面显示是怎么实现的,这个在工作中也比较有用,我们开始做这一块的时候也走了不少的弯路.我记得我 ...

  5. android 电池(二):android关机充电流程、充电画面显示【转】

    本文转载自:http://blog.csdn.net/xubin341719/article/details/8498580 上一篇我们讲了锂电池的充放电的流程和电池的一些特性,这一节我们重点说一下a ...

  6. android 关机充电流程

    点击打开链接 0.主要流程 usb插入通过传递cmdline给init解析从而启动充电进程 1. LK lk\app\aboot\aboot.c update_cmdline----------if ...

  7. Android系统自定义关机充电图标

    需求描述 关机充电图标的修改地址在哪里?替换照片有哪些格式要求? 实现方案 图片路径: system/core/healthd/images/ 关机充电: system/core/healthd/he ...

  8. 【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析

    [Android SDM660源码分析]- 02 - UEFI XBL QcomChargerApp充电流程代码分析 一.加载 UEFI 默认应用程序 1.1 LaunchDefaultBDSApps ...

  9. android开关机日志_(android 关机/重启)Android关机/重启流程解析

    --------------------------------Introduction-------------------------- 1. 在PowerManager的API文档中,给出了一个 ...

  10. Android 12 关机重启流程

    文章托管在gitee上 Android Notes , 同步csdn 本文基于Android12 分析 关机流程 Android上层触发关机的入口很多,但最终几乎都是调用ShutdownThread. ...

最新文章

  1. 【转】statfs获得硬盘使用情况 模拟linux命令 df
  2. MongoDB 条件操作符
  3. 为什么我电脑进入睡眠后网络就断开了?(解决打开睡眠后的笔记本无法连接校园网的问题)
  4. 标准化工作导则2020_夯实标准化工作——标准化工作导则GB/T 1.12020培训会在水发兴业能源顺利举办...
  5. 周报(1.13到1.20)
  6. 安装neptune-client库
  7. matlab 8点fft蝶形图,FFT快速傅里叶变换(蝶形算法)详解精要.ppt
  8. 微信小程序连接蓝牙打印机打印图片示例
  9. java swing漂亮界面框架_开源软件分享-漂亮的JavaFx GUI界面框架
  10. Teams下载安装教程
  11. ON_NOTIFY用法
  12. 定义字符串的两种方式
  13. 羊皮卷的故事-第九章-羊皮卷之二
  14. ssd的smt_探访固态硬盘工厂,揭秘 SSD 生产过程
  15. 微信小程序实现一个简单的加减法的计算器
  16. 初识人工智能身份证识别身份证号码
  17. 基于百度飞桨的单/多镜头行人追踪——PaddleDetection
  18. teamviewer早期版本11-14 版 官方链接地址
  19. 虹科方案|用 Western Digital 和ATTO技术优化 SMR 存储解决方案的大数据工作负载
  20. unity学习笔记-text文本识别html富文本(待改进)

热门文章

  1. [转载] 信息系统项目管理师教程——06 信息化基础知识
  2. zookeeper运维
  3. Scala中I/O类使用详细解析
  4. 解决CAD图层打印不出来的方法
  5. JMeter 如何把上一个请求的结果作为下一个请求的参数 —— 使用正则提取器
  6. nginx 服务器重启命令,关闭(转)
  7. js 的强制 类型 转换cast, 伪对象?
  8. 利用动态图层实现数据的实时显示
  9. [ERROR]-Error: failure: repodata/filelists.xml.gz from addons: [Errno 256] No more mirrors to try.
  10. 用 maven 命令启动项目和直接用tomcat 启动项目的区别