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