Home | History | Annotate | Download | only in healthd
      1 /*
      2  * Copyright (C) 2011-2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <dirent.h>
     18 #include <errno.h>
     19 #include <fcntl.h>
     20 #include <inttypes.h>
     21 #include <stdio.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <sys/epoll.h>
     25 #include <sys/stat.h>
     26 #include <sys/types.h>
     27 #include <sys/un.h>
     28 #include <time.h>
     29 #include <unistd.h>
     30 
     31 #include <functional>
     32 
     33 #include <android-base/file.h>
     34 #include <android-base/macros.h>
     35 
     36 #include <linux/netlink.h>
     37 #include <sys/socket.h>
     38 
     39 #include <cutils/klog.h>
     40 #include <cutils/misc.h>
     41 #include <cutils/properties.h>
     42 #include <cutils/uevent.h>
     43 #include <sys/reboot.h>
     44 
     45 #ifdef CHARGER_ENABLE_SUSPEND
     46 #include <suspend/autosuspend.h>
     47 #endif
     48 
     49 #include "AnimationParser.h"
     50 #include "healthd_draw.h"
     51 
     52 #include <health2/Health.h>
     53 #include <healthd/healthd.h>
     54 
     55 using namespace android;
     56 
     57 char* locale;
     58 
     59 #ifndef max
     60 #define max(a, b) ((a) > (b) ? (a) : (b))
     61 #endif
     62 
     63 #ifndef min
     64 #define min(a, b) ((a) < (b) ? (a) : (b))
     65 #endif
     66 
     67 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
     68 
     69 #define MSEC_PER_SEC (1000LL)
     70 #define NSEC_PER_MSEC (1000000LL)
     71 
     72 #define BATTERY_UNKNOWN_TIME (2 * MSEC_PER_SEC)
     73 #define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
     74 #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
     75 
     76 #define LAST_KMSG_MAX_SZ (32 * 1024)
     77 
     78 #define LOGE(x...) KLOG_ERROR("charger", x);
     79 #define LOGW(x...) KLOG_WARNING("charger", x);
     80 #define LOGV(x...) KLOG_DEBUG("charger", x);
     81 
     82 static constexpr const char* animation_desc_path =
     83     "/res/values/charger/animation.txt";
     84 
     85 struct key_state {
     86     bool pending;
     87     bool down;
     88     int64_t timestamp;
     89 };
     90 
     91 struct charger {
     92     bool have_battery_state;
     93     bool charger_connected;
     94     int64_t next_screen_transition;
     95     int64_t next_key_check;
     96     int64_t next_pwr_check;
     97 
     98     key_state keys[KEY_MAX + 1];
     99 
    100     animation* batt_anim;
    101     GRSurface* surf_unknown;
    102     int boot_min_cap;
    103 };
    104 
    105 static const animation BASE_ANIMATION = {
    106     .text_clock =
    107         {
    108             .pos_x = 0,
    109             .pos_y = 0,
    110 
    111             .color_r = 255,
    112             .color_g = 255,
    113             .color_b = 255,
    114             .color_a = 255,
    115 
    116             .font = nullptr,
    117         },
    118     .text_percent =
    119         {
    120             .pos_x = 0,
    121             .pos_y = 0,
    122 
    123             .color_r = 255,
    124             .color_g = 255,
    125             .color_b = 255,
    126             .color_a = 255,
    127         },
    128 
    129     .run = false,
    130 
    131     .frames = nullptr,
    132     .cur_frame = 0,
    133     .num_frames = 0,
    134     .first_frame_repeats = 2,
    135 
    136     .cur_cycle = 0,
    137     .num_cycles = 3,
    138 
    139     .cur_level = 0,
    140     .cur_status = BATTERY_STATUS_UNKNOWN,
    141 };
    142 
    143 static animation::frame default_animation_frames[] = {
    144     {
    145         .disp_time = 750,
    146         .min_level = 0,
    147         .max_level = 19,
    148         .surface = NULL,
    149     },
    150     {
    151         .disp_time = 750,
    152         .min_level = 0,
    153         .max_level = 39,
    154         .surface = NULL,
    155     },
    156     {
    157         .disp_time = 750,
    158         .min_level = 0,
    159         .max_level = 59,
    160         .surface = NULL,
    161     },
    162     {
    163         .disp_time = 750,
    164         .min_level = 0,
    165         .max_level = 79,
    166         .surface = NULL,
    167     },
    168     {
    169         .disp_time = 750,
    170         .min_level = 80,
    171         .max_level = 95,
    172         .surface = NULL,
    173     },
    174     {
    175         .disp_time = 750,
    176         .min_level = 0,
    177         .max_level = 100,
    178         .surface = NULL,
    179     },
    180 };
    181 
    182 static animation battery_animation = BASE_ANIMATION;
    183 
    184 static charger charger_state;
    185 static healthd_config* healthd_config;
    186 static android::BatteryProperties* batt_prop;
    187 static std::unique_ptr<HealthdDraw> healthd_draw;
    188 
    189 /* current time in milliseconds */
    190 static int64_t curr_time_ms() {
    191     timespec tm;
    192     clock_gettime(CLOCK_MONOTONIC, &tm);
    193     return tm.tv_sec * MSEC_PER_SEC + (tm.tv_nsec / NSEC_PER_MSEC);
    194 }
    195 
    196 #define MAX_KLOG_WRITE_BUF_SZ 256
    197 
    198 static void dump_last_kmsg(void) {
    199     char* buf;
    200     char* ptr;
    201     unsigned sz = 0;
    202     int len;
    203 
    204     LOGW("\n");
    205     LOGW("*************** LAST KMSG ***************\n");
    206     LOGW("\n");
    207     const char* kmsg[] = {
    208         // clang-format off
    209         "/sys/fs/pstore/console-ramoops-0",
    210         "/sys/fs/pstore/console-ramoops",
    211         "/proc/last_kmsg",
    212         // clang-format on
    213     };
    214     for (size_t i = 0; i < arraysize(kmsg); ++i) {
    215         buf = (char*)load_file(kmsg[i], &sz);
    216         if (buf && sz) break;
    217     }
    218 
    219     if (!buf || !sz) {
    220         LOGW("last_kmsg not found. Cold reset?\n");
    221         goto out;
    222     }
    223 
    224     len = min(sz, LAST_KMSG_MAX_SZ);
    225     ptr = buf + (sz - len);
    226 
    227     while (len > 0) {
    228         int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
    229         char yoink;
    230         char* nl;
    231 
    232         nl = (char*)memrchr(ptr, '\n', cnt - 1);
    233         if (nl) cnt = nl - ptr + 1;
    234 
    235         yoink = ptr[cnt];
    236         ptr[cnt] = '\0';
    237         klog_write(6, "<4>%s", ptr);
    238         ptr[cnt] = yoink;
    239 
    240         len -= cnt;
    241         ptr += cnt;
    242     }
    243 
    244     free(buf);
    245 
    246 out:
    247     LOGW("\n");
    248     LOGW("************* END LAST KMSG *************\n");
    249     LOGW("\n");
    250 }
    251 
    252 #ifdef CHARGER_ENABLE_SUSPEND
    253 static int request_suspend(bool enable) {
    254     if (enable)
    255         return autosuspend_enable();
    256     else
    257         return autosuspend_disable();
    258 }
    259 #else
    260 static int request_suspend(bool /*enable*/) {
    261     return 0;
    262 }
    263 #endif
    264 
    265 static void kick_animation(animation* anim) {
    266     anim->run = true;
    267 }
    268 
    269 static void reset_animation(animation* anim) {
    270     anim->cur_cycle = 0;
    271     anim->cur_frame = 0;
    272     anim->run = false;
    273 }
    274 
    275 static void update_screen_state(charger* charger, int64_t now) {
    276     animation* batt_anim = charger->batt_anim;
    277     int disp_time;
    278 
    279     if (!batt_anim->run || now < charger->next_screen_transition) return;
    280 
    281     if (healthd_draw == nullptr) {
    282         if (healthd_config && healthd_config->screen_on) {
    283             if (!healthd_config->screen_on(batt_prop)) {
    284                 LOGV("[%" PRId64 "] leave screen off\n", now);
    285                 batt_anim->run = false;
    286                 charger->next_screen_transition = -1;
    287                 if (charger->charger_connected) request_suspend(true);
    288                 return;
    289             }
    290         }
    291 
    292         healthd_draw.reset(new HealthdDraw(batt_anim));
    293 
    294 #ifndef CHARGER_DISABLE_INIT_BLANK
    295         healthd_draw->blank_screen(true);
    296 #endif
    297     }
    298 
    299     /* animation is over, blank screen and leave */
    300     if (batt_anim->num_cycles > 0 && batt_anim->cur_cycle == batt_anim->num_cycles) {
    301         reset_animation(batt_anim);
    302         charger->next_screen_transition = -1;
    303         healthd_draw->blank_screen(true);
    304         LOGV("[%" PRId64 "] animation done\n", now);
    305         if (charger->charger_connected) request_suspend(true);
    306         return;
    307     }
    308 
    309     disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
    310 
    311     /* unblank the screen on first cycle and first frame */
    312     if (batt_anim->cur_cycle == 0 && batt_anim->cur_frame == 0) healthd_draw->blank_screen(false);
    313 
    314     /* animation starting, set up the animation */
    315     if (batt_anim->cur_frame == 0) {
    316         LOGV("[%" PRId64 "] animation starting\n", now);
    317         if (batt_prop) {
    318             batt_anim->cur_level = batt_prop->batteryLevel;
    319             batt_anim->cur_status = batt_prop->batteryStatus;
    320             if (batt_prop->batteryLevel >= 0 && batt_anim->num_frames != 0) {
    321                 /* find first frame given current battery level */
    322                 for (int i = 0; i < batt_anim->num_frames; i++) {
    323                     if (batt_anim->cur_level >= batt_anim->frames[i].min_level &&
    324                         batt_anim->cur_level <= batt_anim->frames[i].max_level) {
    325                         batt_anim->cur_frame = i;
    326                         break;
    327                     }
    328                 }
    329 
    330                 // repeat the first frame first_frame_repeats times
    331                 disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
    332                             batt_anim->first_frame_repeats;
    333             }
    334         }
    335     }
    336 
    337     /* draw the new frame (@ cur_frame) */
    338     healthd_draw->redraw_screen(charger->batt_anim, charger->surf_unknown);
    339 
    340     /* if we don't have anim frames, we only have one image, so just bump
    341      * the cycle counter and exit
    342      */
    343     if (batt_anim->num_frames == 0 || batt_anim->cur_level < 0) {
    344         LOGW("[%" PRId64 "] animation missing or unknown battery status\n", now);
    345         charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
    346         batt_anim->cur_cycle++;
    347         return;
    348     }
    349 
    350     /* schedule next screen transition */
    351     charger->next_screen_transition = now + disp_time;
    352 
    353     /* advance frame cntr to the next valid frame only if we are charging
    354      * if necessary, advance cycle cntr, and reset frame cntr
    355      */
    356     if (charger->charger_connected) {
    357         batt_anim->cur_frame++;
    358 
    359         while (batt_anim->cur_frame < batt_anim->num_frames &&
    360                (batt_anim->cur_level < batt_anim->frames[batt_anim->cur_frame].min_level ||
    361                 batt_anim->cur_level > batt_anim->frames[batt_anim->cur_frame].max_level)) {
    362             batt_anim->cur_frame++;
    363         }
    364         if (batt_anim->cur_frame >= batt_anim->num_frames) {
    365             batt_anim->cur_cycle++;
    366             batt_anim->cur_frame = 0;
    367 
    368             /* don't reset the cycle counter, since we use that as a signal
    369              * in a test above to check if animation is over
    370              */
    371         }
    372     } else {
    373         /* Stop animating if we're not charging.
    374          * If we stop it immediately instead of going through this loop, then
    375          * the animation would stop somewhere in the middle.
    376          */
    377         batt_anim->cur_frame = 0;
    378         batt_anim->cur_cycle++;
    379     }
    380 }
    381 
    382 static int set_key_callback(charger* charger, int code, int value) {
    383     int64_t now = curr_time_ms();
    384     int down = !!value;
    385 
    386     if (code > KEY_MAX) return -1;
    387 
    388     /* ignore events that don't modify our state */
    389     if (charger->keys[code].down == down) return 0;
    390 
    391     /* only record the down even timestamp, as the amount
    392      * of time the key spent not being pressed is not useful */
    393     if (down) charger->keys[code].timestamp = now;
    394     charger->keys[code].down = down;
    395     charger->keys[code].pending = true;
    396     if (down) {
    397         LOGV("[%" PRId64 "] key[%d] down\n", now, code);
    398     } else {
    399         int64_t duration = now - charger->keys[code].timestamp;
    400         int64_t secs = duration / 1000;
    401         int64_t msecs = duration - secs * 1000;
    402         LOGV("[%" PRId64 "] key[%d] up (was down for %" PRId64 ".%" PRId64 "sec)\n", now, code,
    403              secs, msecs);
    404     }
    405 
    406     return 0;
    407 }
    408 
    409 static void update_input_state(charger* charger, input_event* ev) {
    410     if (ev->type != EV_KEY) return;
    411     set_key_callback(charger, ev->code, ev->value);
    412 }
    413 
    414 static void set_next_key_check(charger* charger, key_state* key, int64_t timeout) {
    415     int64_t then = key->timestamp + timeout;
    416 
    417     if (charger->next_key_check == -1 || then < charger->next_key_check)
    418         charger->next_key_check = then;
    419 }
    420 
    421 static void process_key(charger* charger, int code, int64_t now) {
    422     key_state* key = &charger->keys[code];
    423 
    424     if (code == KEY_POWER) {
    425         if (key->down) {
    426             int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME;
    427             if (now >= reboot_timeout) {
    428                 /* We do not currently support booting from charger mode on
    429                    all devices. Check the property and continue booting or reboot
    430                    accordingly. */
    431                 if (property_get_bool("ro.enable_boot_charger_mode", false)) {
    432                     LOGW("[%" PRId64 "] booting from charger mode\n", now);
    433                     property_set("sys.boot_from_charger_mode", "1");
    434                 } else {
    435                     if (charger->batt_anim->cur_level >= charger->boot_min_cap) {
    436                         LOGW("[%" PRId64 "] rebooting\n", now);
    437                         reboot(RB_AUTOBOOT);
    438                     } else {
    439                         LOGV("[%" PRId64
    440                              "] ignore power-button press, battery level "
    441                              "less than minimum\n",
    442                              now);
    443                     }
    444                 }
    445             } else {
    446                 /* if the key is pressed but timeout hasn't expired,
    447                  * make sure we wake up at the right-ish time to check
    448                  */
    449                 set_next_key_check(charger, key, POWER_ON_KEY_TIME);
    450 
    451                 /* Turn on the display and kick animation on power-key press
    452                  * rather than on key release
    453                  */
    454                 kick_animation(charger->batt_anim);
    455                 request_suspend(false);
    456             }
    457         } else {
    458             /* if the power key got released, force screen state cycle */
    459             if (key->pending) {
    460                 kick_animation(charger->batt_anim);
    461             }
    462         }
    463     }
    464 
    465     key->pending = false;
    466 }
    467 
    468 static void handle_input_state(charger* charger, int64_t now) {
    469     process_key(charger, KEY_POWER, now);
    470 
    471     if (charger->next_key_check != -1 && now > charger->next_key_check)
    472         charger->next_key_check = -1;
    473 }
    474 
    475 static void handle_power_supply_state(charger* charger, int64_t now) {
    476     if (!charger->have_battery_state) return;
    477 
    478     if (!charger->charger_connected) {
    479         /* Last cycle would have stopped at the extreme top of battery-icon
    480          * Need to show the correct level corresponding to capacity.
    481          */
    482         kick_animation(charger->batt_anim);
    483         request_suspend(false);
    484         if (charger->next_pwr_check == -1) {
    485             charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
    486             LOGW("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n",
    487                  now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
    488         } else if (now >= charger->next_pwr_check) {
    489             LOGW("[%" PRId64 "] shutting down\n", now);
    490             reboot(RB_POWER_OFF);
    491         } else {
    492             /* otherwise we already have a shutdown timer scheduled */
    493         }
    494     } else {
    495         /* online supply present, reset shutdown timer if set */
    496         if (charger->next_pwr_check != -1) {
    497             LOGW("[%" PRId64 "] device plugged in: shutdown cancelled\n", now);
    498             kick_animation(charger->batt_anim);
    499         }
    500         charger->next_pwr_check = -1;
    501     }
    502 }
    503 
    504 void healthd_mode_charger_heartbeat() {
    505     charger* charger = &charger_state;
    506     int64_t now = curr_time_ms();
    507 
    508     handle_input_state(charger, now);
    509     handle_power_supply_state(charger, now);
    510 
    511     /* do screen update last in case any of the above want to start
    512      * screen transitions (animations, etc)
    513      */
    514     update_screen_state(charger, now);
    515 }
    516 
    517 void healthd_mode_charger_battery_update(android::BatteryProperties* props) {
    518     charger* charger = &charger_state;
    519 
    520     charger->charger_connected =
    521         props->chargerAcOnline || props->chargerUsbOnline || props->chargerWirelessOnline;
    522 
    523     if (!charger->have_battery_state) {
    524         charger->have_battery_state = true;
    525         charger->next_screen_transition = curr_time_ms() - 1;
    526         reset_animation(charger->batt_anim);
    527         kick_animation(charger->batt_anim);
    528     }
    529     batt_prop = props;
    530 }
    531 
    532 int healthd_mode_charger_preparetowait(void) {
    533     charger* charger = &charger_state;
    534     int64_t now = curr_time_ms();
    535     int64_t next_event = INT64_MAX;
    536     int64_t timeout;
    537 
    538     LOGV("[%" PRId64 "] next screen: %" PRId64 " next key: %" PRId64 " next pwr: %" PRId64 "\n",
    539          now, charger->next_screen_transition, charger->next_key_check, charger->next_pwr_check);
    540 
    541     if (charger->next_screen_transition != -1) next_event = charger->next_screen_transition;
    542     if (charger->next_key_check != -1 && charger->next_key_check < next_event)
    543         next_event = charger->next_key_check;
    544     if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event)
    545         next_event = charger->next_pwr_check;
    546 
    547     if (next_event != -1 && next_event != INT64_MAX)
    548         timeout = max(0, next_event - now);
    549     else
    550         timeout = -1;
    551 
    552     return (int)timeout;
    553 }
    554 
    555 static int input_callback(charger* charger, int fd, unsigned int epevents) {
    556     input_event ev;
    557     int ret;
    558 
    559     ret = ev_get_input(fd, epevents, &ev);
    560     if (ret) return -1;
    561     update_input_state(charger, &ev);
    562     return 0;
    563 }
    564 
    565 static void charger_event_handler(uint32_t /*epevents*/) {
    566     int ret;
    567 
    568     ret = ev_wait(-1);
    569     if (!ret) ev_dispatch();
    570 }
    571 
    572 animation* init_animation() {
    573     bool parse_success;
    574 
    575     std::string content;
    576     if (base::ReadFileToString(animation_desc_path, &content)) {
    577         parse_success = parse_animation_desc(content, &battery_animation);
    578     } else {
    579         LOGW("Could not open animation description at %s\n", animation_desc_path);
    580         parse_success = false;
    581     }
    582 
    583     if (!parse_success) {
    584         LOGW("Could not parse animation description. Using default animation.\n");
    585         battery_animation = BASE_ANIMATION;
    586         battery_animation.animation_file.assign("charger/battery_scale");
    587         battery_animation.frames = default_animation_frames;
    588         battery_animation.num_frames = ARRAY_SIZE(default_animation_frames);
    589     }
    590     if (battery_animation.fail_file.empty()) {
    591         battery_animation.fail_file.assign("charger/battery_fail");
    592     }
    593 
    594     LOGV("Animation Description:\n");
    595     LOGV("  animation: %d %d '%s' (%d)\n", battery_animation.num_cycles,
    596          battery_animation.first_frame_repeats, battery_animation.animation_file.c_str(),
    597          battery_animation.num_frames);
    598     LOGV("  fail_file: '%s'\n", battery_animation.fail_file.c_str());
    599     LOGV("  clock: %d %d %d %d %d %d '%s'\n", battery_animation.text_clock.pos_x,
    600          battery_animation.text_clock.pos_y, battery_animation.text_clock.color_r,
    601          battery_animation.text_clock.color_g, battery_animation.text_clock.color_b,
    602          battery_animation.text_clock.color_a, battery_animation.text_clock.font_file.c_str());
    603     LOGV("  percent: %d %d %d %d %d %d '%s'\n", battery_animation.text_percent.pos_x,
    604          battery_animation.text_percent.pos_y, battery_animation.text_percent.color_r,
    605          battery_animation.text_percent.color_g, battery_animation.text_percent.color_b,
    606          battery_animation.text_percent.color_a, battery_animation.text_percent.font_file.c_str());
    607     for (int i = 0; i < battery_animation.num_frames; i++) {
    608         LOGV("  frame %.2d: %d %d %d\n", i, battery_animation.frames[i].disp_time,
    609              battery_animation.frames[i].min_level, battery_animation.frames[i].max_level);
    610     }
    611 
    612     return &battery_animation;
    613 }
    614 
    615 void healthd_mode_charger_init(struct healthd_config* config) {
    616     using android::hardware::health::V2_0::implementation::Health;
    617 
    618     int ret;
    619     charger* charger = &charger_state;
    620     int i;
    621     int epollfd;
    622 
    623     dump_last_kmsg();
    624 
    625     LOGW("--------------- STARTING CHARGER MODE ---------------\n");
    626 
    627     ret = ev_init(std::bind(&input_callback, charger, std::placeholders::_1, std::placeholders::_2));
    628     if (!ret) {
    629         epollfd = ev_get_epollfd();
    630         healthd_register_event(epollfd, charger_event_handler, EVENT_WAKEUP_FD);
    631     }
    632 
    633     animation* anim = init_animation();
    634     charger->batt_anim = anim;
    635 
    636     ret = res_create_display_surface(anim->fail_file.c_str(), &charger->surf_unknown);
    637     if (ret < 0) {
    638         LOGE("Cannot load custom battery_fail image. Reverting to built in: %d\n", ret);
    639         ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
    640         if (ret < 0) {
    641             LOGE("Cannot load built in battery_fail image\n");
    642             charger->surf_unknown = NULL;
    643         }
    644     }
    645 
    646     GRSurface** scale_frames;
    647     int scale_count;
    648     int scale_fps;  // Not in use (charger/battery_scale doesn't have FPS text
    649                     // chunk). We are using hard-coded frame.disp_time instead.
    650     ret = res_create_multi_display_surface(anim->animation_file.c_str(), &scale_count, &scale_fps,
    651                                            &scale_frames);
    652     if (ret < 0) {
    653         LOGE("Cannot load battery_scale image\n");
    654         anim->num_frames = 0;
    655         anim->num_cycles = 1;
    656     } else if (scale_count != anim->num_frames) {
    657         LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n", scale_count,
    658              anim->num_frames);
    659         anim->num_frames = 0;
    660         anim->num_cycles = 1;
    661     } else {
    662         for (i = 0; i < anim->num_frames; i++) {
    663             anim->frames[i].surface = scale_frames[i];
    664         }
    665     }
    666     ev_sync_key_state(
    667         std::bind(&set_key_callback, charger, std::placeholders::_1, std::placeholders::_2));
    668 
    669     charger->next_screen_transition = -1;
    670     charger->next_key_check = -1;
    671     charger->next_pwr_check = -1;
    672 
    673     // Initialize Health implementation (which initializes the internal BatteryMonitor).
    674     Health::initInstance(config);
    675 
    676     healthd_config = config;
    677     charger->boot_min_cap = config->boot_min_cap;
    678 }
    679